在一个异步处理来自stdin
的输入行的Node.js程序中,如何确保异步处理程序以与输入相同的顺序打印结果?
SSCCE program.js
(依赖:npm install split
):
var executeCommand = function(line) {
setTimeout(function() { console.log(line); }, 1000 * Math.random());
};
var split = require("split");
process.stdin.pipe(split("\n")).on("data", function(line) {
executeCommand(line);
});
正在运行printf "A\nB\nC\nD\nE\nF" | node program.js
生成
B
E
A
D
C
F
这是因为处理程序(executeCommand
)具有不可预测的延迟,此处将其模拟为随机setTimeout
。 “处理”(setTimeout
s)应同时发生,但它们的输出(console.log
s)应与常量输入的顺序相同。
我怎样才能实现这一目标?
我通常只是惊呼“它是Async.js
时间!”,但这次我看不到合适的现有助手:由于任务不断进入,任何操作固定的投入集合不会削减它。
答案 0 :(得分:1)
我明白了。
当@Peter和@jfriend指出时,处理程序结果必须排队到只允许出列已完成任务的队列。每当处理程序完成时,检查已完成任务的好时机。
图片可能会澄清它的工作原理:
原来transform stream是一种很好的模型。 (" Stuff进来,最终与传入的东西相关的东西出现了#34;几乎是对变换流的描述。)每当结果完成时,就会推送完成的任务。
以下是问题的示例,修改为工作:
var Transform = require("stream").Transform;
var split = require("split");
var orderedParallel = function(worker) {
var s = new Transform({ objectMode : true });
var resultsQueue = [];
var sendFinishedFromQueue = function() {
while (resultsQueue[0] && resultsQueue[0].done) {
s.push(resultsQueue.shift().data);
}
}
s._transform = function(chunk, encoding, callback) {
var resultObject = { done : false, data : null };
resultsQueue.push(resultObject);
worker(chunk, function(result) {
resultObject.data = result;
resultObject.done = true;
sendFinishedFromQueue();
});
callback();
};
s._flush = function(callback) {
// Do nothing.
//
// We don't have anything to flush, because as workers complete,
// they'll handle sending any and all messages we're allowed to send
// right now.
};
return s;
};
var executeCommand = function(line, cb) {
setTimeout(function() { cb(line); }, 1000 * Math.random());
};
process.stdin.pipe(split("\n")).pipe(orderedParallel(executeCommand))
.on("data", function(x) { console.log(x); });
为了说服自己有效,请尝试一百个并行任务:
for (( i=0; i<=100; i++ ))
do
echo "$i"
done | node program.js
它们应该并行完成(在1秒内随机完成),但无论如何都要从orderedParallel
变换流中出来。
答案 1 :(得分:0)
async.queue
和worker函数都执行命令并打印结果将执行此操作。你不会有最佳的并发性,但它会表现正常,所以我建议编码,即使它是一个垫脚石。保持正确的行为但添加一些并发性将需要对主要工作函数进行排队,但是在output1到达之前输出2准备好的情况下还需要缓冲输出。