I am running an event loop of the following form:
var i;
var j = 10;
for (i = 0; i < j; i++) {
asynchronousProcess(callbackFunction() {
alert(i);
});
}
I am trying to display a series of alerts showing the numbers 0 through 10. The problem is that by the time the callback function is triggered, the loop has already gone through a few iterations and it displays a higher value of i
. Any recommendations on how to fix this?
This question is related to
javascript
asynchronous
for-loop
synchronization
var i = 0;_x000D_
var length = 10;_x000D_
_x000D_
function for1() {_x000D_
console.log(i);_x000D_
for2();_x000D_
}_x000D_
_x000D_
function for2() {_x000D_
if (i == length) {_x000D_
return false;_x000D_
}_x000D_
setTimeout(function() {_x000D_
i++;_x000D_
for1();_x000D_
}, 500);_x000D_
}_x000D_
for1();
_x000D_
Here is a sample functional approach to what is expected here.
Any recommendation on how to fix this?
Several. You can use bind:
for (i = 0; i < j; i++) {
asycronouseProcess(function (i) {
alert(i);
}.bind(null, i));
}
Or, if your browser supports let (it will be in the next ECMAScript version, however Firefox already supports it since a while) you could have:
for (i = 0; i < j; i++) {
let k = i;
asycronouseProcess(function() {
alert(k);
});
}
Or, you could do the job of bind
manually (in case the browser doesn't support it, but I would say you can implement a shim in that case, it should be in the link above):
for (i = 0; i < j; i++) {
asycronouseProcess(function(i) {
return function () {
alert(i)
}
}(i));
}
I usually prefer let
when I can use it (e.g. for Firefox add-on); otherwise bind
or a custom currying function (that doesn't need a context object).
JavaScript code runs on a single thread, so you cannot principally block to wait for the first loop iteration to complete before beginning the next without seriously impacting page usability.
The solution depends on what you really need. If the example is close to exactly what you need, @Simon's suggestion to pass i
to your async process is a good one.
ES2017: You can wrap the async code inside a function(say XHRPost) returning a promise( Async code inside the promise).
Then call the function(XHRPost) inside the for loop but with the magical Await keyword. :)
let http = new XMLHttpRequest();_x000D_
let url = 'http://sumersin/forum.social.json';_x000D_
_x000D_
function XHRpost(i) {_x000D_
return new Promise(function(resolve) {_x000D_
let params = 'id=nobot&%3Aoperation=social%3AcreateForumPost&subject=Demo' + i + '&message=Here%20is%20the%20Demo&_charset_=UTF-8';_x000D_
http.open('POST', url, true);_x000D_
http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');_x000D_
http.onreadystatechange = function() {_x000D_
console.log("Done " + i + "<<<<>>>>>" + http.readyState);_x000D_
if(http.readyState == 4){_x000D_
console.log('SUCCESS :',i);_x000D_
resolve();_x000D_
}_x000D_
}_x000D_
http.send(params); _x000D_
});_x000D_
}_x000D_
_x000D_
(async () => {_x000D_
for (let i = 1; i < 5; i++) {_x000D_
await XHRpost(i);_x000D_
}_x000D_
})();
_x000D_
async await
is here
(ES7), so you can do this kind of things very easily now.
var i;
var j = 10;
for (i = 0; i < j; i++) {
await asycronouseProcess();
alert(i);
}
Remember, this works only if asycronouseProcess
is returning a Promise
If asycronouseProcess
is not in your control then you can make it return a Promise
by yourself like this
function asyncProcess() {
return new Promise((resolve, reject) => {
asycronouseProcess(()=>{
resolve();
})
})
}
Then replace this line await asycronouseProcess();
by await asyncProcess();
Understanding Promises
before even looking into async await
is must
(Also read about support for async await
)
Source: Stackoverflow.com