Suggest an editImprove this articleRefine the answer for “How do streams (ReadableStream, WritableStream) work?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)A **stream** is a sequence of data that arrives gradually, not all at once. **Key point:** unlike a regular `fetch().text()`, which waits until everything arrives, a stream (`fetch().body`) lets you process data in pieces, as soon as each chunk arrives.Shown above the full answer for quick recall.Answer (EN)Image## What a stream is > A **stream** is a **sequence of data** that arrives **gradually**, > not all at once. The main difference from regular data: - a regular `fetch().text()` waits until **everything arrives** → then returns the result; - a stream (`fetch().body`) lets you **process data in pieces**, **as soon as each part arrives** (chunk by chunk). ## Types of streams in JavaScript | Type | Purpose | |---|---| | **ReadableStream** | a data source (reading) | | **WritableStream** | a data sink (writing) | | **TransformStream** | intermediate processing (reading + writing) | | **DuplexStream** | bidirectional (e.g. a socket) | ## Example: reading data from a stream (ReadableStream) Imagine you're requesting a large file: ```javascript const response = await fetch('https://example.com/large-file.txt'); // response.body is a ReadableStream const reader = response.body.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; console.log('Received a chunk of size:', value.length); } ``` What happens: 1. `fetch` returns a `Response`, whose `.body` is a **ReadableStream**. 2. The `.getReader()` method creates a **reader** that returns data in portions (chunks). 3. `reader.read()` returns a promise with `{ done, value }`. - `value` - a piece of data (type `Uint8Array`), - `done: true` - the stream has ended. This lets you download **gigabytes** without loading everything into RAM. ## Example: writing data to a WritableStream ```javascript const stream = new WritableStream({ write(chunk) { console.log('Writing a chunk:', chunk); }, close() { console.log('Stream finished'); }, }); const writer = stream.getWriter(); await writer.write('Hello'); await writer.write('World'); await writer.close(); ``` Explanation: - `WritableStream` accepts data in parts via `write(chunk)`; - `close()` is called when the stream is finished; - can be used for sending data, writing files, logging, and so on. ## Example: TransformStream (transforming a stream) ```javascript const upperCaseStream = new TransformStream({ transform(chunk, controller) { controller.enqueue(chunk.toUpperCase()); }, }); const writer = upperCaseStream.writable.getWriter(); const reader = upperCaseStream.readable.getReader(); await writer.write('hello '); await writer.write('world!'); await writer.close(); while (true) { const { done, value } = await reader.read(); if (done) break; console.log(value); // "HELLO " "WORLD!" } ``` `TransformStream` connects an input stream (writable) and an output stream (readable), and lets you transform data "on the fly". ## Using streams with pipeTo / pipeThrough JS streams can be connected (piped) like plumbing pipes ```javascript await readableStream .pipeThrough(transformStream) .pipeTo(writableStream); ``` This way: - `pipeThrough` applies a transformation; - `pipeTo` passes the result to the next stream. ## Example with fetch() and TextDecoderStream ```javascript const response = await fetch('https://example.com/data.txt'); const textStream = response.body .pipeThrough(new TextDecoderStream()) // decode bytes into text .pipeThrough( new TransformStream({ transform(chunk, controller) { controller.enqueue(chunk.toUpperCase()); // transform the text }, }) ); const reader = textStream.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; console.log(value); } ``` Now every piece of the file is converted into **text**, then to **uppercase**, and processed right away. ## In Node.js, streams work similarly Node.js has its own implementation (`fs.createReadStream`, `stream.Writable`, etc.), but the concept is the same: ```javascript import fs from 'fs'; const readStream = fs.createReadStream('file.txt', { encoding: 'utf-8' }); readStream.on('data', chunk => console.log('Chunk:', chunk)); readStream.on('end', () => console.log('Done')); ``` Node.js streams are based on **EventEmitter**, while browser streams are based on **promises and controllers**. ## Why streams matter | Benefit | Description | |---|---| | **Performance** | No need to wait for the whole file to load | | **Memory savings** | Processing happens "on the fly", in pieces | | **Composition** | Streams can be chained together | | **Two-way communication** | Used in WebSocket, fetch(), Response, Body | | **Control** | Reading can be paused/resumed | ## A simple analogy > Streams are like a **conveyor belt** > > Data doesn't arrive all at once - > it "rides" along the belt **in portions (chunks)**, > and at each stage (Readable → Transform → Writable) > you can process, filter and store it. ## Summary | Type | Purpose | Usage example | |---|---|---| | **ReadableStream** | A data source | `fetch().body`, `file.stream()` | | **WritableStream** | A data sink | Writing to a file, a log, the network | | **TransformStream** | A transformer | Compression, encryption, filtering | | **pipeTo / pipeThrough** | Connecting streams | `readable.pipeThrough(transform).pipeTo(writable)` |For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.