Asynchronous Process inside a javascript for loop
我正在运行以下形式的事件循环:
1 2 3 4 5 6 7 8 | var i; var j = 10; for (i = 0; i < j; i++) { asynchronousProcess(callbackFunction() { alert(i); }); } |
我正在尝试显示一系列显示数字0到10的警报。问题是,当回调函数被触发时,循环已经经历了几次迭代,并且它显示了更高的
当所有异步操作启动时,
这是因为在继续循环的下一个迭代之前,
要解决这个问题,必须为每个回调单独保存循环索引。在JavaScript中,实现这一点的方法是在函数闭包中捕获它。这可以通过创建一个专门用于此目的的内联函数闭包(下面显示的第一个示例)来完成,也可以创建一个外部函数,将索引传递给它,并让它为您唯一地维护索引(下面显示的第二个示例)。
截至2016年,如果您有一个完全符合规范ES6的javascript实现,您也可以使用
使用.foreach()进行迭代,因为它创建了自己的函数闭包
1 2 3 4 5 | someArray.forEach(function(item, i) { asynchronousProcess(function(item) { console.log(i); }); }); |
使用IIFE创建自己的函数闭包
1 2 3 4 5 6 7 8 9 10 11 | 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); } |
创建或修改外部函数并将变量传递给它
如果您可以修改
1 2 3 4 5 6 | var j = 10; for (var i = 0; i < j; i++) { asynchronousProcess(i, function(cntr) { console.log(cntr); }); } |
使用ES6
如果您有一个完全支持ES6的JavaScript执行环境,那么可以在您的
1 2 3 4 5 6 | const j = 10; for (let i = 0; i < j; i++) { asynchronousProcess(function() { console.log(i); }); } |
在这样的
用承诺和异步/等待进行序列化
如果您的异步函数返回了一个承诺,并且您希望序列化异步操作以逐个运行,而不是并行运行,并且您在支持
1 2 3 4 5 6 7 8 | 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); } } |
这将确保一次只有一个对
Any recommendation on how to fix this?
几个。可以使用bind:
1 2 3 4 5 | for (i = 0; i < j; i++) { asycronouseProcess(function (i) { alert(i); }.bind(null, i)); } |
或者,如果您的浏览器支持let(它将出现在下一个EcmaScript版本中,但是火狐已经支持了一段时间),您可以:
1 2 3 4 5 6 | for (i = 0; i < j; i++) { let k = i; asycronouseProcess(function() { alert(k); }); } |
或者,您可以手动执行
1 2 3 4 5 6 7 | for (i = 0; i < j; i++) { asycronouseProcess(function(i) { return function () { alert(i) } }(i)); } |
我通常喜欢使用
1 2 3 4 5 6 | var i; var j = 10; for (i = 0; i < j; i++) { await asycronouseProcess(); alert(i); } |
记住,只有当
如果
1 2 3 4 5 6 7 | function asyncProcess() { return new Promise((resolve, reject) => { asycronouseProcess(()=>{ resolve(); }) }) } |
然后用
在调查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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)中(异步代码在承诺中)。
然后调用for循环内的函数(xhrpost),但使用神奇的wait关键字。:)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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); } })(); |
JavaScript代码运行在一个线程上,因此在开始下一个循环之前,您不能主要阻止等待第一个循环迭代完成,而不会严重影响页面可用性。
解决办法取决于你真正需要什么。如果示例与您所需要的非常接近,@simon建议将