【问题标题】:Track completion of javascript callbacks without nesting functions在没有嵌套函数的情况下跟踪 javascript 回调的完成
【发布时间】:2015-10-01 01:36:38
【问题描述】:

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

一些伪代码可能会有所帮助:

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 具有不可变类型,因此它不会更改getStuffdone 的值。

那么……我该怎么办?

【问题讨论】:

  • 澄清一下,done 不是不可变类型,它是一个通过值而不是通过引用传递的原语。这是一个很好的区别,但我认为需要注意这一点。
  • 你在什么背景下?这是 node.js 吗?您可以通过使用承诺并在所有完成后执行回调来解决此问题,这应该是一种更优雅的处理方式。
  • @Sosdoc,没关系。在 node.js 和客户端 JS 中,异步回调的标准相同。
  • @PatrickRoberts 我只是要求指出一个使用 Promise 的可能解决方案,以防这不是节点(但我怀疑)它可以通过使用 jquery 来完成。
  • @Sosdoc 我在节点中。

标签: javascript node.js callback closures


【解决方案1】:

您可以只定义一次 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));
    }
}

【讨论】:

  • 如果我要使用这种方法,我也会亲自将 results 全球化到闭包,而不是将其传递给每个函数。
  • 那么当getStuff在匿名函数内部时,我该如何调用它呢?
  • 我实际上才意识到这只会让getStuff 工作一次。也许我的编辑不是那么优雅。
  • @PatrickRoberts 也许如果 doneresults 在闭包中定义,但它们的值是在 getStuff 中设置的?
  • 可能在 getStuff 中定义 myCallback、done 和 results,但不在循环中?
【解决方案2】:

这是一个使用带 Q 的 Promise 的解决方案

首先,用 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 及其所有可能的用法,它可以避免出现大量嵌套回调(通常称为“回调地狱”)的恶劣情况

【讨论】:

  • 绝对更优雅。稍后我会研究它,谢谢!
猜你喜欢
  • 2012-12-30
  • 1970-01-01
  • 2013-08-03
  • 1970-01-01
  • 2014-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多