Backend Development 39 min read

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.

Taobao Frontend Technology
Taobao Frontend Technology
Taobao Frontend Technology
Inside Node.js Readable Streams: Initialization, Modes, and Back‑Pressure Mechanics

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.Readable

and must override the

_read

method 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

_read

to 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

data

event.

Paused Mode

In paused mode the stream does not emit data automatically; the consumer must register a

readable

event and call

read()

to retrieve data.

The internal state is stored in

this._readableState

and 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

readable

listener 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

readable

event.

Flowing Mode Details

Registering a

data

listener switches the stream to flowing mode. The

resume

method triggers an initial

stream.read(0)

and then repeatedly calls

stream.read()

inside a

flow

loop, emitting each chunk through the

data

event.

Example: the same custom readable emits

data

events, 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

flow

loop;

resume()

restores it.

Personal Understanding

In paused mode the “normal” state is reached when the internal buffer reaches

highWaterMark

and 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.

JavaScriptNode.jsStream APIBackpressureReadable Stream
Taobao Frontend Technology
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.