-
-
Notifications
You must be signed in to change notification settings - Fork 34.2k
Description
Version
v16.8.0
Platform
Microsoft Windows NT 10.0.19043.0 x64
Subsystem
stream
What steps will reproduce the bug?
Returning a promise from Readable._construct has the same effect as calling the callback. This is not documented and may be a bug?
This leads to the following strange behaviour:
const {Readable} = require('stream');
const stream = new Readable({
async construct(callback) {
callback();
},
read(size) {
console.log('read', size);
},
destroy(err, callback) {
console.log('destroy', err);
}
});Output:
destroy Error [ERR_MULTIPLE_CALLBACK]: Callback called multiple times
at new NodeError (node:internal/errors:371:5)
at onConstruct (node:internal/streams/destroy:255:37)
at processTicksAndRejections (node:internal/process/task_queues:82:21) {
code: 'ERR_MULTIPLE_CALLBACK'
}
Similarly:
const {Readable} = require('stream');
const stream = new Readable({
async construct(callback) {
// don't call the callback
},
read(size) {
console.log('read');
}
});
stream.read();Output:
read
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior?
Flow should not proceed to Readable._read until after the callback passed to Readble._construct has been called.
The docs state:
This optional function will be scheduled in the next tick by the stream constructor, delaying any _read() and _destroy() calls until callback is called.
What do you see instead?
- In the first example, an error is thrown for calling the callback twice (despite only calling it once)
- In the second example, flow passes to
Readable._readdespite the callback not being called yet.
Additional information
It is desirable to be able to be able to use await within Readable._construct, but labelling this function as async (and therefore returning a Promise) appears to cause the callback function to be implicitly called (which is sometimes unwanted).
As a workaround, I can return my own Promise from Readable._construct and use its resolve/reject functions in lieu of the callback.
In summary, either this is expected behaviour and the docs are incorrect - or vice versa. It's hard to tell.