在没有嵌套函数的情况下跟踪 javascript 回调的完成

Posted

技术标签:

【中文标题】在没有嵌套函数的情况下跟踪 javascript 回调的完成【英文标题】:Track completion of javascript callbacks without nesting functions 【发布时间】: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 我在节点中。 【参考方案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 及其所有可能的用法,它可以避免出现大量嵌套回调(通常称为“回调地狱”)的恶劣情况

【讨论】:

绝对更优雅。稍后我会研究它,谢谢!

以上是关于在没有嵌套函数的情况下跟踪 javascript 回调的完成的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有外部递归函数的情况下解析多个嵌套的 JSON 键?

在没有 Javascript 的情况下使用 Google Analytics?

将嵌套数组与Javascript中的唯一元素合并

在 JSX 中调用 javascript 函数:为啥在没有 () 的情况下调用函数?

如何产生结果 Javascript 嵌套延迟

前端逼死强迫症系列之javascript续集