【发布时间】:2020-07-13 23:08:15
【问题描述】:
这是我在节点 v14.4.0 中提炼成最小的、可重现的示例的更大过程的一部分。在这段代码中,它不会从 for 循环内部输出任何内容。
我在控制台中只看到这个输出:
before for() loop
finished
finally
done
for await (const line1 of rl1) 循环永远不会进入 for 循环 - 它只是跳过它:
const fs = require('fs');
const readline = require('readline');
const { once } = require('events');
async function test(file1, file2) {
try {
const stream1 = fs.createReadStream(file1);
await once(stream1, 'open');
const rl1 = readline.createInterface({input: stream1, crlfDelay: Infinity});
const stream2 = fs.createReadStream(file2);
await once(stream2, 'open');
const rl2 = readline.createInterface({input: stream2, crlfDelay: Infinity});
console.log('before for() loop');
for await (const line1 of rl1) {
console.log(line1);
}
console.log('finished');
} finally {
console.log('finally');
}
}
test("data/numbers.txt", "data/letters.txt").then(() => {
console.log(`done`);
}).catch(err => {
console.log('Got rejected promise:', err);
})
但是,如果我删除了await once(stream, 'open') 语句中的任何一个,那么for 循环将完全按照预期执行(列出rl1 文件的所有行)。因此,显然,来自 readline 接口和流之间的异步迭代器存在一些计时问题。任何想法可能发生的事情。知道是什么导致了这个问题或如何解决它吗?
仅供参考,await once(stream, 'open') 之所以存在,是因为异步迭代器中存在另一个错误,如果打开文件时出现问题,它不会拒绝,而如果文件无法打开,await once(stream, 'open') 会导致您正确获得拒绝打开(基本上是在飞行前打开)。
如果您想知道为什么存在 stream2 代码,它在较大的项目中使用,但我已将此示例缩减为最小的、可重现的示例,并且只需要这么多代码来演示问题.
编辑:在尝试稍微不同的实现时,我发现如果我将两个once(stream, "open") 调用组合在一个Promise.all() 中,它就会起作用。所以,这行得通:
const fs = require('fs');
const readline = require('readline');
const { once } = require('events');
async function test(file1, file2) {
try {
const stream1 = fs.createReadStream(file1);
const rl1 = readline.createInterface({input: stream1, crlfDelay: Infinity});
const stream2 = fs.createReadStream(file2);
const rl2 = readline.createInterface({input: stream2, crlfDelay: Infinity});
// pre-flight file open to catch any open errors here
// because of existing bug in async iterator with file open errors
await Promise.all([once(stream1, "open"), once(stream2, "open")]);
console.log('before for() loop');
for await (const line1 of rl1) {
console.log(line1);
}
console.log('finished');
} finally {
console.log('finally');
}
}
test("data/numbers.txt", "data/letters.txt").then(() => {
console.log(`done`);
}).catch(err => {
console.log('Got rejected promise:', err);
});
这显然不应该对您等待文件打开的确切方式敏感。某处存在一些计时错误。我想在 readline 或 readStream 上找到该错误并将其归档。有什么想法吗?
【问题讨论】:
-
我看到一个现有的May 18th issue 提出了类似的问题。我已将我的示例添加到该问题中。而且,另一个 related issue 从 7 月 5 日开始。
-
还有,这里的the issue from last December 导致我必须输入
await once(stream, 'open')才能在打开文件时正确捕获错误。 -
December bug 源于这个 stackoverflow 问题:How to handle error from fs readline interface async iterator。
-
根据您的两个代码 sn-ps 之间的差异,我的第一个想法是在将读取流附加到 readline 接口之前触发了
data事件。在对流执行某些操作之前等待open可能会产生影响,因为事件发射器是同步的,而承诺解析不是。 -
@JakeHolzinger - 是否记录了您必须立即使用它而无需任何干预异步操作?而且,为什么 ASYNCHRONOUS 迭代器要求您不能在其他异步操作中使用它?而且,它还有其他错误。因此,我遇到的许多人宁愿自己重新实现逐行处理(或使用外部模块)而不是使用它。当您插入一行代码并且由于一些未记录的时间原因而中断时,这只是一个损坏的设计。它应该被固定为可靠或删除。
标签: javascript node.js promise readline async-iterator