NodeJS回调序列

时间:2014-09-09 23:27:43

标签: node.js asynchronous underscore.js promise

民间,   我有以下功能,我想知道只有当数据库操作在所有项目上完成时才调用callback()的正确方法:

function mapSomething (callback) {
    _.each(someArray, function (item) {
        dao.dosomething(item.foo, function (err, account) {
            item.email = account.email;
        });
    });
    callback();
},

我需要迭代someArray并为每个元素执行数据库调用。替换数组中的所有项后,我只需要调用回调。当然,回调现在是在错误的地方

谢谢!

2 个答案:

答案 0 :(得分:3)

您目前拥有它的方式,callback在任何(异步)任务完成之前执行。

async模块有each(),可以进行最终回调:

var async = require('async');

// ...

function mapSomething (callback) {
  async.each(someArray, function(item, cb) {
    dao.dosomething(item.foo, function(err, account) {
      if (err)
        return cb(err);
      item.email = account.email;
      cb();
    });
  }, callback);
}

答案 1 :(得分:2)

在调用callback()之前,这不会等待所有数据库调用完成。它将同时并行启动所有数据库调用(我假设dao.dosomething()是什么)。然后,在任何数据库调用完成之前立即调用callback()

您有多种选择来解决问题。

  1. 您可以使用promises(通过promisifying数据库调用),然后使用Promise.all()等待所有数据库调用完成。
  2. 您可以使用async库来管理协调。
  3. 您可以自己跟踪每个人完成的时间以及最后一个完成时,请致电您的回电。
  4. 我建议使用选项1.或2.就个人而言,我更喜欢使用promises,因为你正在与数据库接口,这可能不是你进行数据库调用的唯一地方,所以我会宣传接口(bluebird会在一个函数调用中为你做这个),然后使用promises。

    这是承诺解决方案的样子:

    var Promise = require('bluebird');
    
    // make promise version of your DB function
    // ideally, you'd promisify the whole DB API with .promisifyAll()
    var dosomething = Promise.promisify(dao.dosomething, dao);
    
    function mapSomething (callback, errCallback) {
        Promise.all(_.map(someArray, function(item) {
            return dosomething(item.foo).then(function (account) {
                item.email = account.email;
            });
        }).then(callback, errCallback);
    }
    

    这假设您希望并行运行所有数据库调用,然后在完成后调用回调。

    仅供参考,这是a link to how Bluebird promisify's现有的API。我将这种机制用于节点中的所有异步文件I / O,它节省了大量的编码时间,使错误处理更加清晰。异步回调是正确错误处理的噩梦,特别是如果可以从异步回调中抛出异常。


    P.S。您实际上可能希望您的mapSomething()函数只返回一个promise本身,因此调用者负责指定他们自己的.then()处理程序,它允许调用者使用返回的promise来与其他东西同步(例如,这种方式更加灵活。)

    function mapSomething() {
        return Promise.all(_.map(someArray, function(item) {
            return dosomething(item.foo).then(function (account) {
                item.email = account.email;
            });
        })
    }
    
    mapSomething.then(mapSucessHandler, mapErrorHandler);
    

    我自己没有尝试过Bluebird的.map(),但是一旦你宣传了数据库调用,我认为它会更简化它:

    function mapSomething() {
        return Promise.map(someArray, function(item) {
            return dosomething(item.foo).then(function (account) {
                item.email = account.email;
            });
        })
    }
    
    mapSomething.then(mapSucessHandler, mapErrorHandler);