2015-07-12 50 views
4

因此,我正在编写一个函数,它会产生一堆数据库调用。我想将它们的结果存储在一个数组中,并在完成后触发回调。无嵌套函数的javascript回调跟踪完成

一些伪代码可以帮助:

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有一个不变的类型,所以它不getStuff改变done值。

那么...我该怎么办?

+0

讨厌的情况下,为了澄清,“完成”不是不可变的类型,它是一个通过价值传递而非通过引用传递的原语。这是一个很好的区别,但我认为重要的是要注意。 –

+0

你在什么环境?这是node.js?你可以通过使用承诺和执行回调来实现这一点,它应该是一个更优雅的处理方式。 – Sosdoc

+1

@Sosdoc,没关系。在node.js和客户端JS中,同样的异步回调标准也适用。 –

回答

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

我个人将'results'全局化到闭包,而不是将它传递给每个函数,如果我要使用这种方法。 –

+0

@PatrickRoberts同意。更新 – yts

+0

那么,当它在匿名函数中时如何调用'getStuff'? – Cheezey

2

下面是使用承诺符合Q

首先的一个解决方案,安装Q随故宫

npm install q 

要记住,要求其

var Q = require('q'); 

那么你的最终代码可能be this

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上阅读了其所有可能的用途,这样可避免在那里你有很多嵌套的回调(通常被称为“回调地狱”)

+0

绝对更优雅。我稍后再来看看,谢谢! – Cheezey