如何确保以相同顺序输出流命令的结果?

时间:2014-12-07 22:54:58

标签: node.js asynchronous stream

在一个异步处理来自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时间!”,但这次我看不到合适的现有助手:由于任务不断进入,任何操作固定的投入集合不会削减它。

2 个答案:

答案 0 :(得分:1)

我明白了。

@Peter@jfriend指出时,处理程序结果必须排队到只允许出列已完成任务的队列。每当处理程序完成时,检查已完成任务的好时机。

图片可能会澄清它的工作原理:

Parallel processing with a queue

原来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)

限制为1的

async.queue和worker函数都执行命令并打印结果将执行此操作。你不会有最佳的并发性,但它会表现正常,所以我建议编码,即使它是一个垫脚石。保持正确的行为但添加一些并发性将需要对主要工作函数进行排队,但是在output1到达之前输出2准备好的情况下还需要缓冲输出。