等待最后一个stream.on(' data')事件中的异步函数回调

时间:2015-12-25 16:09:37

标签: node.js stream redis

我正在使用fast-csv使用stream迭代CSV文件。对于CSV文件的每一行,我想在redis中创建一个作业,我使用kue。解析一行是同步函数。整件事看起来像这样:

var csvStream = fastCsv(_config.csvOptions)
  .on('data', function(data) {
    var stream = this;
    stream.pause();

    var payload = parseRow(data, _config);
    console.log(payload); // the payload is always printed to the console

    var job = kue.create('csv-row', {
        payload: payload
      })
      .save(function(err) {
        if (!err) console.log('Enqueued at ' + job.id);
        else console.log('Redis error ' + JSON.stringify(err));

        stream.resume();
      });
  })
  .on('end', function() {
    callback(); // this ends my parsing
  });

我的CSV文件的每一行都显示简单console.log(payload);,但未创建作业。即,save回调中的所有输出都没有打印出来,而且作业不在我的redis中。

我认为,因为它是CSV文件的最后一行,所以流已经发出end,因此在流程终止之前无法执行最后一个kue.create()

有没有办法暂停流end直到kue完成?

2 个答案:

答案 0 :(得分:6)

您可以使用async library解决此问题。您可以将以下模式用于任何流。

var AsyncLib = require('async');

var worker = function (payload, cb) {
    //do something with payload and call callback
    return cb();
};

var concurrency = 5;
var streamQueue = AsyncLib.queue(worker, concurrency);

var stream = //some readable stream;

stream.on('data', function(data) {
    //no need to pause and resume
    var payload = '//some payload';
    streamQueue.push(payload);
})
.on('end', function() {
    //register drain event on end and callback
    streamQueue.drain = function () {
        callback();
    };
});

答案 1 :(得分:4)

我刚刚发现自己处于同样的境地。我用Go语言中的sync.WaitGroup模式解决了它。

在最简单的形式中,它看起来像(函数将返回Promise):

function process() {
    const locker = {
        items: 0,
        resolve: null,
        lock: function() { this.items++ },
        unlock: function() {
            if (this.items > 0) this.items--;
            if (this.items === 0) {
                this.resolve()
                this.resolve = null
            }
        },
    };
    return new Promise(function(resolve, reject) {
        locker.resolve = resolve;
        locker.lock();

        fastCsv(_config.csvOptions)
        .on('end', function() { locker.unlock(); })
        .on('data', function(data) {
            locker.lock();
            const payload = parseRow(data, _config);
            kue.create('csv-row', { payload: payload })
                .save(function(err) {
                    if (!err) console.log('Enqueued at ' + job.id);
                    else console.log('Redis error ' + JSON.stringify(err));
                    locker.unlock();
                });
        });

    });
}

process().then(function () {
    console.log('Now ALL data processed!');
});

在实际应用程序中,您可以将locker提取到不同的类/模块,添加错误处理等......但原理是相同的 - 等到不仅流是空的,而且还要创建所有非阻塞操作它完成了。