0

在创建流 (A)、创建另一个流 (B) 和读取流 (B) 之后,读取过程从流 (A) 停止。我怎么解决这个问题?

Node.js v14.18.1

import * as readline from 'readline';
import { Readable } from 'stream';

async function  main() {

    const  streamA = Readable.from('a');
    const  readerA = readline.createInterface({
        input: streamA,
        crlfDelay: Infinity
    });

    var  stopCase = false;
    if (stopCase) {

        const  streamB = Readable.from('b');
        const  readerB = readline.createInterface({
            input: streamB,
            crlfDelay: Infinity
        });

        console.log('readB');
        for await (const line of readerB) {
            console.log(line);
        }
    }
    console.log(`readerA.closed = ${'closed' in readerA}`);

    console.log('readA');
    for await (const line of readerA) {
        console.log(line);
    }
    console.log('success');
}
main();

输出(stopCase=true):

readB
b
readerA.closed = true
readA

输出(stopCase=false):

readerA.closed = false
readA
a
success
4

1 回答 1

0

问题是,一旦你这样做:

const readerA = readline.createInterface({
    input: streamA,
    crlfDelay: Infinity
});

然后,streamA现在准备好流动,并readerA准备好在您点击事件循环时立即生成事件。当您进入stopCase块并点击 时for await (const line of readerB),这将允许streamA流动,这将允许readerA触发事件。

但是,当 readerA 事件触发时,您并没有在监听它们,因此它streamA会在您不监听时完成它所拥有的内容。

readerA如果您在完成stopCase块之后才创建,您可以看到它如何更好地工作。因为当你击中块的内部时,那时streamAreaderA没有流动。awaitstopCase

这就是我所说的由于试图将承诺添加到事件驱动的流中而引起的日益增长的痛苦。如果你让流处于流动状态并且你打算用来await读取这些事件,但是你await还有其他一些承诺,那么当你还没有收听时,你在第一个流上的所有事件都会触发。它不知道您正在等待使用await它。您将其设置为流动,以便解释器一旦到达事件循环,它就会开始流动,即使您没有使用await.

我之前在自己的代码中遇到过这个问题,解决方案是在您即将使用await它来读取它或者直到您配置了一个更传统的事件处理程序来监听任何事件之前,不要将流设置为流动那个流。基本上,您不能配置两个流for await (...)同时使用。配置一个流,将其与您的 一起使用for await (...),然后配置另一个。并且,还要注意在处理for await (...)循环时使用的任何其他承诺。使用该结构时有很多方法可以搞砸。

在我看来,如果流实际上被置于不同的状态以与 Promise 一起使用,它会更可靠地工作,因此它只会通过 Promise 接口流动。那么,这种事情就不会发生了。但是,我确信该实施也存在许多挑战。

例如,如果您这样做:

import * as readline from 'readline';
import { Readable } from 'stream';

async function main() {
    var stopCase = true;
    console.log(`stopCase = ${stopCase}`);
    if (stopCase) {

        const streamB = Readable.from('b');
        const readerB = readline.createInterface({
            input: streamB,
            crlfDelay: Infinity
        });

        console.log('readB');
        for await (const line of readerB) {
            console.log(line);
        }
    }
    const streamA = Readable.from('a');
    const readerA = readline.createInterface({
        input: streamA,
        crlfDelay: Infinity
    });
    console.log(`streamA flowing = ${streamA.readableFlowing}`);
    console.log(`readerA.closed = ${!!readerA.closed}`);

    console.log('readA');
    for await (const line of readerA) {
        console.log(line);
    }
    console.log('success');
}
main();

然后,您将获得所有输出:

stopCase = true
readB
b
streamA flowing = true
readerA.closed = false
readA
a
success

您永远不会得到的原因console.log('success')可能是因为您遇到了for await (const line of readerA) { ...}循环,并且由于没有更多数据的承诺而停在那里。同时,nodejs 注意到进程中没有任何东西可以创建任何未来事件,因此它退出了进程。

您可以在一个更简单的应用程序中看到相同的概念:

async function main() {
    await new Promise(resolve => {
        // do nothing
    });
    console.log('success');
}
main();

它等待一个永远不会完成的承诺,并且应用程序中没有创建任何东西的事件,因此 nodejs 只是关闭了 ever logging success

于 2021-12-03T07:06:11.847 回答