在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,似乎支持。