javascript for循环中的异步进程
我正在运行以下形式的事件循环:javascript for循环中的异步进程,javascript,asynchronous,for-loop,synchronization,Javascript,Asynchronous,For Loop,Synchronization,我正在运行以下形式的事件循环: var i; var j = 10; for (i = 0; i < j; i++) { asynchronousProcess(callbackFunction() { alert(i); }); } vari; var j=10; 对于(i=0;i
var i;
var j = 10;
for (i = 0; i < j; i++) {
asynchronousProcess(callbackFunction() {
alert(i);
});
}
vari;
var j=10;
对于(i=0;i
我试图显示一系列显示数字0到10的警报。问题是,在触发回调函数时,循环已经经过了几次迭代,并且它显示了更高的值
i
。关于如何修复此问题的任何建议?JavaScript代码在单个线程上运行,因此在开始下一个循环迭代之前,您不能阻止等待第一个循环迭代完成,而不会严重影响页面可用性
解决方案取决于你真正需要什么。如果示例与您需要的非常接近,@Simon建议将
i
传递给您的异步进程,这是一个很好的建议。for循环在启动所有异步操作时立即运行到完成。当他们在将来某个时间完成并调用回调时,循环索引变量i
的值将是所有回调的最后一个值
这是因为for
循环在继续循环的下一次迭代之前不会等待异步操作完成,而且异步回调将在将来某个时候调用。因此,循环完成其迭代,然后在异步操作完成时调用回调。因此,循环索引是“完成”的,并且对于所有回调都位于其最终值
为了解决这个问题,您必须为每个回调单独保存循环索引。在Javascript中,实现这一点的方法是在函数闭包中捕获它。这可以通过创建一个专门用于此目的的内联函数闭包来实现(下面显示的第一个示例),也可以创建一个外部函数,将索引传递给它,并让它为您唯一地维护索引(下面显示的第二个示例)
从2016年起,如果您有一个完全符合规范的Javascript ES6实现,您还可以使用let
来定义for
循环变量,它将为for
循环的每次迭代唯一定义(下面的第三个实现)。但是,请注意,这是ES6实现中的延迟实现特性,因此您必须确保您的执行环境支持该选项
使用.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);
}
使用ES6let
如果您有一个完全支持ES6的Javascript执行环境,您可以在for
循环中使用let
,如下所示:
var j = 10;
for (var i = 0; i < j; i++) {
asynchronousProcess(i, function(cntr) {
console.log(cntr);
});
}
const j = 10;
for (let i = 0; i < j; i++) {
asynchronousProcess(function() {
console.log(i);
});
}
这将确保一次只有一个对asynchronousProcess()
的调用处于运行状态,并且for
循环在每个调用完成之前都不会前进。这与之前所有并行运行异步操作的方案不同,因此它完全取决于您想要的设计。注意:await
与承诺一起工作,因此您的函数必须返回一个承诺,该承诺在异步操作完成时被解析/拒绝。另外,请注意,要使用wait
,必须将包含函数声明为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);
});
function someFunction(){
让承诺=[];
for(设i=0;i<10;i++){
promises.push(返回spromise()的异步进程);
}
返回承诺。全部(承诺);
}
someFunction()。然后(结果=>{
//按顺序排列的结果数组
控制台日志(结果);
}).catch(错误=>{
控制台日志(err);
});
有没有关于如何解决这个问题的建议
好几个。您可以使用:
我通常更喜欢
let
当我可以使用它时(例如Firefox插件);否则,bind
或自定义函数(不需要上下文对象)。异步等待
在这里
(ES7),所以你现在可以很容易地做这类事情
var i;
var j = 10;
for (i = 0; i < j; i++) {
await asycronouseProcess();
alert(i);
}
然后替换此行等待AsycroouseProcess()代码>通过等待异步进程()代码>
了解承诺
甚至在查看异步等待
之前都是必须的
(另请阅读有关支持异步等待的内容)
var i=0;
变量长度=10;
函数for1(){
控制台日志(i);
for2();
}
函数2(){
如果(i==长度){
返回false;
}
setTimeout(函数(){
i++;
for1();
}, 500);
}
for1()代码>ES2017:您可以将异步代码封装在返回承诺(承诺中的异步代码)的函数(比如XHRPost)中
然后在for循环内调用函数(XHRPost),但使用神奇的wait关键字。:)
让http=newXMLHttpRequest();
让url为空http://sumersin/forum.social.json';
函数XHRpost(i){
返回新承诺(函数(解析){
让params='id=nobot&%3Aoperation=social%3AcreateForumPost&subject=Demo'+i+'&message=Here%20是%20Demo&'u charset=UTF-8';
http.open('POST',url,true);
http.setRequestHeader('Content-type','application/x-www-form-urlencoded');
http.onreadystatechange=函数(){
log(“完成”+i+“>”+http.readyState);
如果(http.readyState==4){
console.log('SUCCESS:',i);
解决();
}
}
http.send(params);
});
}
(异步()=>{
for(设i=1;i<5;i++){
等待XHRpost(i);
}
})();
将i参数添加到异步进程
函数中如何?如果您可以修改asycronouseProcess()
函数,则可以将其传递给callbackfunction添加的第二个选项
for (i = 0; i < j; i++) {
asycronouseProcess(function (i) {
alert(i);
}.bind(null, i));
}
for (i = 0; i < j; i++) {
let k = i;
asycronouseProcess(function() {
alert(k);
});
}
for (i = 0; i < j; i++) {
asycronouseProcess(function(i) {
return function () {
alert(i)
}
}(i));
}
var i;
var j = 10;
for (i = 0; i < j; i++) {
await asycronouseProcess();
alert(i);
}
function asyncProcess() {
return new Promise((resolve, reject) => {
asycronouseProcess(()=>{
resolve();
})
})
}