我目前有此工作代码(此处略有简化);这是将readline包装在promise中的标准习惯用法。
function process(fname, options){
return new Promise(function (resolve, reject) {
const stats = {cnt:0,error:0,mse:0.0}
const reader = require('readline').createInterface({
input: fs.createReadStream(fname),
})
reader.on('error', reject) // Reject the promise, when an error
reader.on('line', function (row) {
stats.error += doSync(row)
stats.cnt++
})
reader.on('close', function () {
if (stats.cnt >= 1)stats.mse = stats.error / stats.cnt
resolve(stats)
})
})
}
我想将呼叫doSync()
更改为doAsync()
(这将返回一个承诺)。
我尝试更改此设置:
reader.on('line', function (row) {
stats.error += doSync(row)
stats.cnt++
})
收件人:
reader.on('line', async function (row) {
stats.error += await doAsync(row)
stats.cnt++
})
但是没有用。具体来说,它将处理doAsync()
直到异步的实际事物(对shell命令的调用),然后立即继续进行下一行。对于文件中的所有行。然后脚本坐在那里,我们互相瞪着眼睛。
我的直觉是readline忽略了返回的承诺,我无能为力。但我希望蜂巢的想法有一些想法。
我在节点8.12.0上,但是升级到10.x并非不可能。 而且我不限于使用readline。 (但我只能逐行处理输入文件!)
更新:
注意:我的doAsync()
原来有错误。但是,即使解决了问题,readline仍然无法正常工作。
从readline切换到逐行修复了该问题。 (这几乎是一个直接的替代;但是将“关闭”更改为“结束”事件。)可接受的答案是更多的代码,但同样有效。
在比较测试中,Transform
方法花费了1m 48s至1m49s,逐行方法花费了1m 49s至1m 51s。 (注意:每次只运行两次,但这足以说服我他们基本上是相同的。)
使用reader.pause()
/ resume()
对阅读线没有帮助,逐行也不需要(使用它确实可以一次执行严格的处理,但是如果没有,它仍然可以正常工作)。
答案 0 :(得分:1)
使用Transform
流可能是异步处理每一行的最佳方法。
const {Transform} = require('stream');
您可以通过简单的转换替换readline
库:
function toLines() {
let line = '';
return new Transform({
decodeStrings: false,
readableObjectMode: true,
transform(chunk, encoding, callback) {
const lines = chunk.split('\n');
line += lines.shift();
while (lines.length) {
this.push(line);
line = lines.shift();
}
callback();
},
flush(callback) {
if (line) {
this.push(line);
}
callback();
}
});
}
并实施另一个收集“统计信息”的Transform
:
function toStats() {
const stats = {cnt: 0, error: 0, mse: 0.0};
return new Transform({
objectMode: true,
async transform(line, encoding, callback) {
stats.error += await doAsync(line);
stats.cnt++;
callback();
},
flush(callback) {
if (stats.cnt >= 1)stats.mse = stats.error / stats.cnt;
callback(null, stats);
}
});
}
然后,您可以实现process
来利用转换:
async function process(fname, options) {
return new Promise((resolve, reject) => {
fs.createReadStream(fname, {encoding: 'utf8'})
.pipe(toLines())
.pipe(toStats())
.on('error', reject)
.on('data', resolve);
});
}