我有以下代码:
for(var i = 0; i < list.length; i++){
mc_cli.get(list[i], function(err, response) {
do_something(i);
});
}
mc_cli
是与memcached数据库的连接。可以想象,回调函数是异步的,因此可以在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循环的最后一个值。
任何人都可以告诉我闭嘴的错误是什么?我以为我理解了它们,但我无法理解为什么这不起作用。
答案 0 :(得分:76)
由于您正在运行数组,因此您只需使用提供列表项的forEach
和回调中的索引。迭代将有自己的范围。
list.forEach(function(listItem, index){
mc_cli.get(listItem, function(err, response) {
do_something(index);
});
});
答案 1 :(得分:41)
这是异步函数内部循环范例,我通常使用立即调用的匿名函数来处理它。这可以确保使用索引变量的正确值调用异步函数。
好的,太好了。所以所有异步函数都已启动,循环退出。现在,由于它们的异步性质或它们将以何种顺序完成,因此无法确定这些功能何时完成。如果您的代码需要等到所有这些函数在执行之前完成,我建议您简单地计算已完成的函数数量:
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 :).');
}
答案 2 :(得分:14)
你非常接近,但你应该将闭包传递给get
而不是把它放在回调中:
function createCallback(i) {
return function(){
do_something(i);
}
}
for(var i = 0; i < list.length; i++){
mc_cli.get(list[i], createCallback(i));
}
答案 3 :(得分:13)
我知道这是一个老线程,但无论如何添加我的答案。 ES2015 let
具有在每次迭代时重新绑定循环变量的功能,因此它在异步回调中维护循环变量的值,因此您可以尝试以下方法:
for(let i = 0; i < list.length; i++){
mc_cli.get(list[i], function(err, response) {
do_something(i);
});
}
但无论如何,使用forEach
或使用immediate-invoked-function创建闭包更好,因为let
是ES2015功能,可能不支持所有浏览器和实现。从Bindings ->let->for/for-in loop iteration scope
下的here开始,我可以看到它不支持 Edge 13 ,甚至直到 Firefox 49 (我还没有&#39;在这些浏览器中检查)。它甚至表示它不支持Node 4,但我亲自测试过它似乎得到了支持。
答案 4 :(得分:4)
尝试使用async/await
语法和Promise
(async function() {
for(var i = 0; i < list.length; i++){
await new Promise(next => {
mc_cli.get(list[i], function(err, response) {
do_something(i); next()
})
})
}
})()
这将在每个周期中停止循环,直到触发next()
功能
答案 5 :(得分:2)
ES2017:您可以将异步代码包装在返回诺言的函数中(例如XHRPost)。
然后在for循环中调用函数(XHRPost),但使用神奇的Await关键字。 :)
let http = new XMLHttpRequest();
let url = 'http://sumersin/forum.social.json';
function XHRpost(i) {
return new Promise(function(resolve) {
let params = 'id=nobot&%3Aoperation=social%3AcreateForumPost&subject=Demo' + i + '&message=Here%20is%20the%20Demo&_charset_=UTF-8';
http.open('POST', url, true);
http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
http.onreadystatechange = function() {
console.log("Done " + i + "<<<<>>>>>" + http.readyState);
if(http.readyState == 4){
console.log('SUCCESS :',i);
resolve();
}
}
http.send(params);
});
}
for (let i = 1; i < 5; i++) {
await XHRpost(i);
}
答案 6 :(得分:0)
如果你想在循环中运行异步函数,但仍想在回调执行后保留索引或其他变量,你可以将代码包装在IIFE(立即调用的函数表达式)中。
var arr = ['Hello', 'World', 'Javascript', 'Async', ':)'];
for( var i = 0; i < arr.length; i++) {
(function(index){
setTimeout(function(){
console.log(arr[index]);
}, 500);
答案 7 :(得分:0)
(async function() {
for(var i = 1; i < category.length; i++){
let result = await $.ajax({
url: '{{ route('admin.loyalty-commission.sub-category') }}', // Url of the Route
data: {id:category[i-1], cat_state:i, next_cat:category[i]},
success: function (data) {
console.log('ajax success previous category id' + category[i-1]);
// Check if the logic was successful or not
if (data.status == 'success') {
$(data.subcategories).appendTo(sub_category); //ajax result append to sub_category
} else {
console.log(data.msg);
}
},
error: function (data) {
// Error while calling the controller (HTTP Response Code different as 200 OK
console.log('Error:', data);
success = false;
}
});
}
})()
答案 8 :(得分:-1)
这是来自我的应用程序的示例代码。它可能有助于解决问题。 我在地图循环中使用了 async/await。在一个数组中解决了多个承诺。
这帮我解决了这个问题 JavaScript async and await in loops
const refObjId= ['Account', 'Contact', 'Group'];
const readdirPro = file => {
return new Promise((resolve, reject) => {
fs.readdir(file, (err, data) => {
if (err) reject('I could not find the file');
resolve(data);
});
});
};
const fileNamePromises = refObjId.map(async el => {
const fileName = await readdirPro(`${__dirname}/../csv-files/${el}/data`);
return fileName;
});
//fileNamePromises is an array of promises
const fileArr = await Promise.all(fileNamePromises);
console.log(fileArr);