在JavaScript中的for循环中调用一个asynchronous函数

我有以下代码:

for(var i = 0; i < list.length; i++){ mc_cli.get(list[i], function(err, response) { do_something(i); }); } 

mc_cli是到memcached数据库的连接。 正如你可以想象的那样,callback函数是asynchronous的,因此可以在for循环已经结束时执行。 另外,当以这种方式调用do_something(i)它总是使用for循环的最后一个值。

我以这种方式尝试了封闭

 do_something((function(x){return x})(i)) 

但显然这又一次使用了for循环索引的最后一个值。

我也尝试在for循环之前声明一个函数,如下所示:

 var create_closure = function(i) { return function() { return i; } } 

然后打电话

 do_something(create_closure(i)()) 

但是又一次没有成功,返回值始终是for循环的最后一个值。

有人可以告诉我,我做什么closures错了吗? 我以为我了解他们,但我不明白为什么这是行不通的。

既然你正在运行一个数组,你可以简单地使用forEach来提供列表项和callback中的索引。 迭代将有其自己的范围。

 list.forEach(function(listItem, index){ mc_cli.get(listItem, function(err, response) { do_something(index); }); }); 

这是asynchronous函数内循环模式,我通常使用立即调用的匿名函数来处理它。 这确保asynchronous函数被调用了正确的索引variables值。

好,太好了 所以所有的asynchronous函数都被启动了,循环结束。 现在,由于它们的asynchronous性,或者它们将以什么样的顺序完成,所以这些function何时完成是没有意义的。 如果在执行之前需要等待所有这些function都已经完成的代码,我build议保留一个简单的计数function已经完成的function:

 var total = parsed_result.list.length; var count = 0; for(var i = 0; i < total; i++){ (function(foo){ mc_cli.get(parsed_result.list[foo], function(err, response) { do_something(foo); count++; if (count > total - 1) done(); }); }(i)); } // You can guarantee that this function will not be called until ALL of the // asynchronous functions have completed. function done() { console.log('All data has been loaded :).'); } 

你非常接近,但你应该通过闭包,而不是把它放在callback中:

 function createCallback(i) { return function(){ do_something(i); } } for(var i = 0; i < list.length; i++){ mc_cli.get(list[i], createCallback(i)); } 

我知道这是一个古老的线程,但无论如何增加我的答案。 ES2015具有在每次迭代中重新绑定循环variables的特性,因此它在asynchronouscallback中保持循环variables的值,因此您可以尝试下面的代码:

 for(let i = 0; i < list.length; i++){ mc_cli.get(list[i], function(err, response) { do_something(i); }); } 

但无论如何,最好使用forEach或使用立即调用函数来创build闭包,因为let是ES2015的特性,可能不支持所有的浏览器和实现。 从Bindings ->let->for/for-in loop iteration scope我可以看到它直到Edge 13甚至直到Firefox 49 (我没有在这些浏览器中检查过)都不支持。 它甚至说它不支持节点4,但我个人testing,似乎支持。