-
-
Notifications
You must be signed in to change notification settings - Fork 33.6k
Description
- Version: 11.2.0
- Platform: Windows 10 x64
- Subsystem: Streams
Description
After a readable handler has been added and removed from a stream, the stream no longer begins / resumes flowing when a data handler is added to the stream. This is a breaking change from 10.x and appears inconsistent with the (convoluted) intended behavior of streams. (More on this below.)
Related issues: #22209, #24366, #24281
Example
const stream = fs.createReadStream('foo.txt'); // some file that exists
const onData = () => { console.log('DATA'); };
const onReadable = () => {
console.log('READABLE');
stream.off('readable', onReadable);
stream.on('data', onData);
//stream.resume(); // <-- this causes data to be emitted
};
stream.on('readable', onReadable);Expected Output:
The readable handler is called, and then the data handler is called.
Actual behavior:
The readable handler is called, but not the data handler.
Further Discussion
It is desirable for a stream to resume flowing when no readable handler is attached and a data handler is added. For example, one might generate a stream and check that it successfully opens (via the readable handler) before returning the stream to be consumed elsewhere (via the data handler). This worked at least up through 10.x.
With a certain reading, the current gap in behavior could be held as consistent with the "Three States" / "under the hood" explanation of stream modes, although that in itself may contradict the "Two Reading Modes" abstraction.
From streams documentation:
Two Reading Modes
All Readable streams begin in paused mode but can be switched to flowing mode in one of the following ways: (1) Adding a
dataevent handler...
Adding a
readableevent handler automatically makes the stream to stop flowing, and the data to be consumed viareadable.read(). If thereadableevent handler is removed, then the stream will start flowing again if there is adataevent handler.
Three States
Calling
readable.pause(),readable.unpipe(), or receiving backpressure will cause thereadable.readableFlowingto be set asfalse, temporarily halting the flowing of events but not halting the generation of data. While in this state, attaching a listener for the 'data' event will not switchreadable.readableFlowingtotrue.
Without having looked at code, I interpret the "Three States" description to indicate that there is no way to return from the readableFlowing == false state to the readableFlowing == null state. Hence, adding a readable handler destroys the auto-start behavior of the data handler.
This is sufficiently counter-intuitive that a slew of issues have been filed in the past few months, such as #24366 and #24281. In fact, a patch was even accepted in #22209 that ensures the auto-start behavior of a data handler IFF it was added before the readable handler.