Skip to content

Stream destruction breaks async-iteration and ends nodejs event loop #23730

Closed
@mika-fischer

Description

@mika-fischer
  • Version: v10.12.0
  • Platform: Windows 10 64-bits
  • Subsystem: streams

Async iteration over a readable stream breaks horribly if for any reason the stream is destroyed while the iteration is still running (or even if it is destroyed before starting the iteration). I would expect that the iteration runs normally or throws an exception. What happens instead is that the whole event loop is shut down.

Most notably this makes async iteration useless with pipeline.

Test code:

const {createReadStream} = require('fs');
const {pipeline} = require('stream');
const {createDeflate} = require('zlib');

async function iterate(name, stream, destroy = false) {
    try {
        console.log(`${name}: Starting loop`);
        for await (const chunk of stream) {
            console.log(`${name}: Got a chunk`);
            if (destroy) {
                console.log(`${name}: destroying stream`);
                stream.destroy();
            }
        }
        console.log(`${name}: loop ended`);
    } catch (err) {
        console.log(`${name}: loop ended with error: ${err}`);
    }
}

async function main() {
    // These work fine
    await iterate('oneStream', createReadStream('test.js'));
    await iterate('oneStreamError', createReadStream('doesnotexist'));

    // Any of these just shuts down the node event loop...
    await iterate('oneStreamDestroy', createReadStream('test.js'), true);
    await iterate('pipeline', pipeline(createReadStream('doesnotexist'), createDeflate(), err => console.log(`pipeline ended: ${err}`)));
    const stream = createReadStream('test.js');
    stream.destroy();
    await iterate('destroyedStream', stream);
}

main().catch(console.error);

Output:

> node test.js
oneStream: Starting loop
(node:21664) ExperimentalWarning: Readable[Symbol.asyncIterator] is an experimental feature. This feature could change at any time
oneStream: Got a chunk
oneStream: loop ended
oneStreamError: Starting loop
oneStreamError: loop ended with error: Error: ENOENT: no such file or directory, open 'C:\Users\mfischer\src\tests\node-stream-async-iteration\doesnotexist'
oneStreamDestroy: Starting loop
oneStreamDestroy: Got a chunk
oneStreamDestroy: destroying stream

Metadata

Metadata

Assignees

Labels

confirmed-bugIssues with confirmed bugs.streamIssues and PRs related to the stream subsystem.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions