Inside Node.js Readable Streams: Initialization, Modes, and Back‑Pressure Mechanics
This article dissects Node.js Readable streams, explaining their initialization, internal state management, the distinction between flowing and paused modes, and how back‑pressure is handled, all illustrated with real source‑code examples and diagrams.
Introduction
Stream is the abstract interface for handling streaming data in Node.js and underlies HTTP request and response objects. Node.js defines four basic stream types—Readable, Writable, Duplex, and Transform. This article examines the internal execution mechanism of Readable streams by analysing the source code of Node.js v16.2.0.
Readable Initialization
All readable streams implement
stream.Readableand must override the
_readmethod to fetch data from an underlying resource pool.
<code>const Readable = require('stream').Readable;
class CustomReadable extends Readable {
constructor(dataSource, options) {
super(options);
this.dataSource = dataSource;
}
// Must override _read to call push and read from the underlying source
_read() {
const data = this.dataSource.makeData();
const result = this.push(data);
console.log('Can push more data:', result);
}
}
</code>Note: a readable stream has no data of its own; it must implement
_readto push data into its internal buffer.
Two Modes
Readable streams operate in either flowing or paused mode, which determines how data chunks are delivered to consumers.
Flowing Mode
In flowing mode the stream automatically reads data and emits it via the
dataevent.
Paused Mode
In paused mode the stream does not emit data automatically; the consumer must register a
readableevent and call
read()to retrieve data.
The internal state is stored in
this._readableStateand includes properties such as
highWaterMark,
buffer,
length,
flowing,
ended, and others that control the lifecycle.
Paused Mode Details
The lifecycle can be described with five states: start, reading, normal, ended, and endEmitted. The
highWaterMark(default 16 KB) defines the buffer water level; when the buffer size is below this value the stream reads more data.
Example: a custom readable with a simulated source of 25 000 bytes that pushes 5 000‑byte chunks.
<code>const dataSource = {
data: new Array(25000).fill('1'),
makeData() {
if (!dataSource.data.length) return null;
return dataSource.data.splice(dataSource.data.length - 5000)
.reduce((a, b) => a + '' + b);
}
};
const customReadable = new CustomReadable(dataSource);
</code>When a
readablelistener is added, the stream enters the reading state, fetches data via
_read, pushes it into the buffer, and then repeatedly calls
stream.read(0)(via
maybeReadMore) until the buffer reaches the high‑water mark, after which it emits a
readableevent.
Flowing Mode Details
Registering a
datalistener switches the stream to flowing mode. The
resumemethod triggers an initial
stream.read(0)and then repeatedly calls
stream.read()inside a
flowloop, emitting each chunk through the
dataevent.
Example: the same custom readable emits
dataevents, pauses after 10 000 bytes, waits one second, then resumes.
<code>let consumer = '';
customReadable.on('data', (chunk) => {
consumer += chunk;
if (consumer.length === 10000) {
console.log('**Pause stream**');
customReadable.pause();
setTimeout(() => {
console.log('**Resume stream**');
customReadable.resume();
}, 1000);
}
});
</code>Calling
pause()sets
state.flowing = false, stopping the
flowloop;
resume()restores it.
Personal Understanding
In paused mode the “normal” state is reached when the internal buffer reaches
highWaterMarkand no further reads are needed. In flowing mode the “normal” state is when the consumer explicitly pauses the stream, shifting control of back‑pressure to the consumer.
Notes and References
Diagrams and source files are linked in the original article. References include several deep‑dive posts on Node.js stream internals.
Taobao Frontend Technology
The frontend landscape is constantly evolving, with rapid innovations across familiar languages. Like us, your understanding of the frontend is continually refreshed. Join us on Taobao, a vibrant, all‑encompassing platform, to uncover limitless potential.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.