# Streams API

The Streams API allows you to programmatically access streams of data received over the network or created by other means and process them chunk by chunk. This is efficient for handling large datasets without holding everything in memory.

## 1. The Concept

Traditionally, if you wanted to process a resource (like a video, a text file, or a JSON response), you had to wait for the entire resource to download before you could do anything with it.

With Streams, you can start processing the data as soon as the first chunk arrives.

## 2. Readable Streams

A `ReadableStream` represents a source of data from which you can read.

### Consuming a Stream (Fetch API)

The Fetch API `Response.body` is a `ReadableStream`.

```javascript
fetch('https://example.com/large-text.txt')
  .then(response => {
    const reader = response.body.getReader();
    const decoder = new TextDecoder();

    return new ReadableStream({
      start(controller) {
        function push() {
          reader.read().then(({ done, value }) => {
            if (done) {
              controller.close();
              return;
            }
            
            // Process the chunk (value is a Uint8Array)
            const chunk = decoder.decode(value, { stream: true });
            console.log('Received chunk:', chunk);
            
            controller.enqueue(value);
            push();
          });
        }
        push();
      }
    });
  })
  .then(stream => new Response(stream))
  .then(response => response.text())
  .then(text => console.log('Final text length:', text.length));
```

### Creating a Readable Stream

You can create your own stream to push data to a consumer.

```javascript
const stream = new ReadableStream({
  start(controller) {
    controller.enqueue("Hello ");
    controller.enqueue("World");
    controller.close();
  }
});
```

## 3. Writable Streams

A `WritableStream` represents a destination for data.

```javascript
const writableStream = new WritableStream({
  write(chunk) {
    console.log('Writing chunk:', chunk);
  },
  close() {
    console.log('Stream closed');
  },
  abort(err) {
    console.error('Stream error:', err);
  }
});

const writer = writableStream.getWriter();
writer.write('Hello');
writer.write('Streams');
writer.close();
```

## 4. Transform Streams

A `TransformStream` consists of a pair of streams: a writable stream (input) and a readable stream (output). It allows you to take data, transform it, and pass it along.

```javascript
const transformStream = new TransformStream({
  transform(chunk, controller) {
    controller.enqueue(chunk.toUpperCase());
  }
});

const writableStream = transformStream.writable;
const readableStream = transformStream.readable;
```

## 5. Piping

The real power comes from connecting streams together using `pipeTo()` (Readable -> Writable) or `pipeThrough()` (Readable -> Transform -> Readable).

```javascript
// Example: Fetch -> Decode -> Log
fetch('data.txt').then(async (response) => {
  const stream = response.body
    .pipeThrough(new TextDecoderStream()) // Transform bytes to text
    .pipeTo(new WritableStream({
      write(chunk) {
        console.log(chunk);
      }
    }));
});
```

[[programming/javascript/vanilla/javascript]]
[[programming/javascript/vanilla/ajax-fetch]]
[[programming/javascript/vanilla/promises-async-await]]