Readable Streams
What is a Readable stream?
Section titled “What is a Readable stream?”A Readable is a stream from which data can be consumed. Node’s built-in examples include fs.createReadStream (reading a file), process.stdin (terminal input), and HTTP request bodies. You can also create your own readable sources with Readable.from or by extending Readable.
Readable streams operate in two modes:
- Flowing mode — data events fire continuously as chunks arrive. You attach a
datalistener to enter this mode. - Paused mode — chunks accumulate until you explicitly call
.read(). This is the default until you attach a listener or pipe.
The cleanest modern approach is async iteration with for await...of, which handles paused mode and backpressure automatically.
The data, end, and error events
Section titled “The data, end, and error events”The classic event-based API predates async iteration. It remains important to understand because many Node APIs still emit these events:
const fs = require('node:fs');
const stream = fs.createReadStream('./file.txt', { encoding: 'utf8' });
stream.on('data', function(chunk) { console.log('received chunk of length', chunk.length);});
stream.on('end', function() { console.log('no more data');});
stream.on('error', function(err) { console.error('stream error', err.message);});data— fires each time a chunk is available. The chunk is aBufferby default, or a string ifencodingis set.end— fires once when there is no more data to consume.error— fires if something goes wrong (file missing, permission denied, etc.).
Readable.from
Section titled “Readable.from”Readable.from(iterable) converts any sync or async iterable into a Readable stream. It is useful for testing and for wrapping in-memory data sources:
const { Readable } = require('node:stream');
const stream = Readable.from(['chunk1', 'chunk2', 'chunk3']);The iterable can be a plain array, a generator function, or an async generator — giving you a clean way to produce data lazily.
Async iteration with for await
Section titled “Async iteration with for await”Since Node 10+, all Readable streams implement the async iterator protocol. This means you can consume them with for await...of inside an async function:
async function processStream(stream) { for await (const chunk of stream) { // handle each chunk }}This approach is easier to read than event listeners, handles backpressure automatically, and surfaces errors as thrown exceptions that your try/catch can catch.
Runnable demo
Section titled “Runnable demo”The demo below creates a Readable from an async generator, then consumes it with for await. Each generator yield represents one chunk arriving asynchronously.
Needs the Node.js runtime — open in StackBlitz to run.