I am running an event loop of the following form:
var i;
var j = 10;
for (i = 0; i < j; i++) {
asycronouseProcess(callBackFunction() {
alert(i);
});
}
The for
loop runs immediately to completion while all your asynchronous operations are started. When they complete some time in the future and call their callbacks, the value of your loop index variable i
will be at its last value for all the callbacks.
This is because the for
loop does not wait for an asynchronous operation to complete before continuing on to the next iteration of the loop and because the async callbacks are called some time in the future. Thus, the loop completes its iterations and THEN the callbacks get called when those async operations finish. As such, the loop index is "done" and sitting at its final value for all the callbacks.
To work around this, you have to uniquely save the loop index separately for each callback. In Javascript, the way to do that is to capture it in a function closure. That can either be done be creating an inline function closure specifically for this purpose (first example shown below) or you can create an external function that you pass the index to and let it maintain the index uniquely for you (second example shown below).
As of 2016, if you have a fully up-to-spec ES6 implementation of Javascript, you can also use let
to define the for
loop variable and it will be uniquely defined for each iteration of the for
loop (third implementation below). But, note this is a late implementation feature in ES6 implementations so you have to make sure your execution environment supports that option.
Create Your Own Function Closure Using an 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
asycronouseProcess(function() {
console.log(cntr);
});
})(i);
}
Create or Modify External Function and Pass it the Variable
If you can modify the asycronouseProcess()
function, then you could just pass the value in there and have the asycronouseProcess()
function the cntr back to the callback like this:
var j = 10;
for (var i = 0; i < j; i++) {
asycronouseProcess(i, function(cntr) {
console.log(cntr);
});
}
Use ES6 let
If you have a Javascript execution environment that fully supports ES6, you can use let
in your for
loop like this:
const j = 10;
for (let i = 0; i < j; i++) {
asycronouseProcess(function() {
console.log(i);
});
}
Note: This ES6 feature is often one of the later ES6 features for JS engines to support so you have to make sure this specific feature is supported in your environment.