跟踪javascript回调的完成,无需嵌套函数

时间:2015-07-12 20:39:28

标签: javascript node.js callback closures

所以我正在编写一个能够进行大量数据库调用的函数。我想将结果存储在数组中,并在完成后触发回调。

某些伪代码可能有所帮助:

function getStuff (array, callback) {
    var results = [];
    var done = 0;
    for (var i = 0, len = array.length; i < len; i++) {
        database.fetchOne(array[i], function(result) {
            results[i] = result;
            done++;
            if (done == len)
                callback(results);
        });
    }
}

这很有效。但是,我被告知在一个循环中嵌套一个闭包是不好的做法,因为它会在每次迭代时不断定义函数,并且会产生性能成本。

其他答案建议将回调移到循环之外:

function getStuff (array, callback) {
    var results = [];
    var done = 0;
    for (var i = 0, len = array.length; i < len; i++) {
        database.fetchOne(array[i], myCallback.bind(this, i, results, done, callback));
    }
}

function myCallback (i, results, done, callback, result) {
    results[i] = result;
    done++;
    if (done == len)
        callback(results);
}

但是这不起作用,因为done具有不可变类型,因此它不会更改donegetStuff的值。

那么......我该怎么做?

2 个答案:

答案 0 :(得分:3)

您可以只定义一次myCallback,而不是每次迭代。

function getStuff (array, callback) {
    var results = [];
    var done = 0;

    function myCallback(i, callback, result) { 
        // update results and done in here
    }
    for (var i = 0, len = array.length; i < len; i++) {
        database.fetchOne(array[i], myCallback.bind(this, i, results, done, callback));
    }
}

答案 1 :(得分:2)

这是使用带有Q的承诺的解决方案

首先,使用npm

安装Q.
npm install q

记得要求它

var Q = require('q');

那么你的最终代码可能就是这个

function getStuff (array, callback) {
    //denodeify transforms a node function into one that works with promises
    var fetch = Q.denodeify(database.fetchOne);
    // all waits for all promises to be resolved
    var promise = Q.all(array.map(fetch));

    // callback receives an array with all the return values from fetchOne
    promise.then(callback, function(error) {
        //this gets called in case any of the calls has an error
    });
}

在我看来,这是一个更优雅的解决方案,我建议阅读Q及其所有可能的用法,它可以避免令人讨厌的情况,你有很多嵌套回调(通常被称为&#39;回调地狱&#39; )