提问人:Ben Pearce 提问时间:7/15/2012 最后编辑:pushkinBen Pearce 更新时间:1/5/2020 访问量:227363
javascript for 循环中的异步进程 [duplicate]
Asynchronous Process inside a javascript for loop [duplicate]
问:
社群在 12 个月前审查了是否重新打开这个问题,并关闭了这个问题:
重复这个问题已经得到回答,不是唯一的,也没有与另一个问题区分开来。
我正在运行以下形式的事件循环:
var i;
var j = 10;
for (i = 0; i < j; i++) {
asynchronousProcess(callbackFunction() {
alert(i);
});
}
我正在尝试显示一系列显示数字 0 到 10 的警报。问题在于,当触发回调函数时,循环已经经历了几次迭代,并且它显示了更高的值 。关于如何解决这个问题的任何建议?i
答:
JavaScript 代码在单个线程上运行,因此您不能在不严重影响页面可用性的情况下,主要阻止等待第一次循环迭代完成,然后再开始下一个循环迭代。
解决方案取决于您的真正需求。如果该示例完全接近您需要的内容,那么 @Simon 建议传递给您的异步进程是一个很好的建议。i
循环将立即运行直至完成,同时启动所有异步操作。当它们在将来的某个时间完成并调用其回调时,循环索引变量的值将处于所有回调的最后一个值。for
i
这是因为循环不会等待异步操作完成,然后继续循环的下一次迭代,并且异步回调会在将来的某个时间调用。因此,循环完成其迭代,然后在这些异步操作完成时调用回调。因此,循环索引是“完成”的,并且位于所有回调的最终值处。for
要解决此问题,您必须为每个回调单独保存循环索引。在 Javascript 中,执行此操作的方法是在函数闭包中捕获它。这可以通过专门为此目的创建一个内联函数闭包来完成(下面显示的第一个示例),也可以创建一个将索引传递给的外部函数,并让它为您唯一地维护索引(下面显示的第二个示例)。
截至 2016 年,如果您有一个完全符合规范的 Javascript ES6 实现,您还可以用于定义循环变量,它将为循环的每次迭代唯一定义(下面的第三个实现)。但是,请注意,这是 ES6 实现中的后期实现功能,因此您必须确保执行环境支持该选项。let
for
for
使用 .forEach() 进行迭代,因为它创建了自己的函数闭包
someArray.forEach(function(item, i) {
asynchronousProcess(function(item) {
console.log(i);
});
});
使用 IIFE 创建自己的函数闭包
var j = 10;
for (var i = 0; i < j; i++) {
(function(cntr) {
// here the value of i was passed into as the argument cntr
// and will be captured in this function closure so each
// iteration of the loop can have it's own value
asynchronousProcess(function() {
console.log(cntr);
});
})(i);
}
创建或修改外部函数并向其传递变量
如果你可以修改该函数,那么你可以将值传递到那里,并将函数 cntr 返回回调,如下所示:asynchronousProcess()
asynchronousProcess()
var j = 10;
for (var i = 0; i < j; i++) {
asynchronousProcess(i, function(cntr) {
console.log(cntr);
});
}
使用 ES6 let
如果你有一个完全支持 ES6 的 Javascript 执行环境,你可以在循环中使用,如下所示:let
for
const j = 10;
for (let i = 0; i < j; i++) {
asynchronousProcess(function() {
console.log(i);
});
}
let
在这样的循环声明中声明将为每个循环调用创建一个唯一的值(这是你想要的)。for
i
使用 promise 和 async/await 进行序列化
如果您的异步函数返回一个 promise,并且您希望序列化您的异步操作以一个接一个地运行,而不是并行运行,并且您在支持 和 的新式环境中运行,那么您有更多选择。async
await
async function someFunction() {
const j = 10;
for (let i = 0; i < j; i++) {
// wait for the promise to resolve before advancing the for loop
await asynchronousProcess();
console.log(i);
}
}
这将确保一次只有一个调用在运行中,并且在每个调用完成之前,循环甚至不会前进。这与以前的方案不同,以前的方案都并行运行异步操作,因此这完全取决于所需的设计。注意:使用 promise,因此您的函数必须返回一个 promise,该 promise 在异步操作完成时被解析/拒绝。另外,请注意,为了使用 ,必须声明包含函数。asynchronousProcess()
for
await
await
async
并行运行异步操作,并使用 Promise.all()
按顺序收集结果
function someFunction() {
let promises = [];
for (let i = 0; i < 10; i++) {
promises.push(asynchonousProcessThatReturnsPromise());
}
return Promise.all(promises);
}
someFunction().then(results => {
// array of results in order here
console.log(results);
}).catch(err => {
console.log(err);
});
评论
asycronouseProcess()
j
for/of
关于如何解决这个问题的任何建议?
几个。您可以使用 bind:
for (i = 0; i < j; i++) {
asycronouseProcess(function (i) {
alert(i);
}.bind(null, i));
}
或者,如果你的浏览器支持 let(它将在下一个 ECMAScript 版本中,但 Firefox 已经支持它了),你可以有:
for (i = 0; i < j; i++) {
let k = i;
asycronouseProcess(function() {
alert(k);
});
}
或者,您可以手动完成这项工作(以防浏览器不支持它,但我想说您可以在这种情况下实现垫片,它应该在上面的链接中):bind
for (i = 0; i < j; i++) {
asycronouseProcess(function(i) {
return function () {
alert(i)
}
}(i));
}
我通常更喜欢在我可以使用它的时候(例如,用于Firefox附加组件);否则或自定义 currying 函数(不需要上下文对象)。let
bind
评论
let
asyncronouseProcess
asyncronouseProcess
function asycronouseProcess(fn){ setTimeout(fn, 100);}
async await
在这里
(ES7),所以你现在可以很容易地做这种事情。
var i;
var j = 10;
for (i = 0; i < j; i++) {
await asycronouseProcess();
alert(i);
}
请记住,仅当 asycronouseProcess
返回 Promise
时,这才有效
如果不受您的控制,那么您可以像这样自己让它返回asycronouseProcess
Promise
function asyncProcess() {
return new Promise((resolve, reject) => {
asycronouseProcess(()=>{
resolve();
})
})
}
然后将这一行替换为await asycronouseProcess();
await asyncProcess();
在研究异步 await
之前了解 Promise
是必须的(另请阅读async await
)
评论
var i = 0;
var length = 10;
function for1() {
console.log(i);
for2();
}
function for2() {
if (i == length) {
return false;
}
setTimeout(function() {
i++;
for1();
}, 500);
}
for1();
下面是此处预期的示例功能方法。
ES2017:你可以将异步代码包装在一个函数中(比如 XHRPost),返回一个 promise( promise 中的异步代码)。
然后在 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);
});
}
(async () => {
for (let i = 1; i < 5; i++) {
await XHRpost(i);
}
})();
评论
asynchronousProcess
i