Monday 25 April 2011

JavaScript Async Loop

Something that I've needed several times in the last months is calling a JavaScript asynchronous function several consecutive times. This is not a normal loop, as we have to wait for the async function (that invokes a callback once it's done) to return in order to call the next iteration. I've needed this for invoking several animations in a row, where every animation has to wait for the previous one to finish before starting. I've also used it for doing consecutive Ajax calls.
Usually I create an object with a counter and a function that is passed as callback to the async function to be invoked once it's done.

so we would have an object like this:

var runner = {
curVal: 0,
endVal: 10,
run: function(){
if (this.curVal == this.endVal)
return;
var self = this;
codeFunc(this.curVal, 1000, function(){self.run()});
++this.curVal;
}
};
runner.run();


to control the invocation of a function like this:


var codeFunc = function(i, delay, callback){
printf("execution " + i + " started " );
setTimeout(function(){
printf("execution " + i + " finished");
callback();
},delay);
};


It would be elegant to use some sort of loop construct for these cases, so after some thinking I've come up with this factory function that simulates a loop:


//@incFunc: loop increment-decrement
//@codeFunc: code to be run "inside the loop", it's a function with a signature like this: codeFunc(i, additionalValues, callback)
//@args: arguments to codeFunc
function steppedFor(startVal, endVal, incFunc, codeFunc, args){
var curVal = startVal;
return function myLoop(){
if(curVal == endVal)
return;
var totalArgs = [curVal];
for(var i=0; i≶args.length; i++)
totalArgs.push(args[i]);
totalArgs.push(myLoop);
codeFunc.apply(this, totalArgs);
curVal = incFunc(curVal);
};
}


that will be used like this:


(steppedFor(0, 5, function(i){return ++i;}, function(i, delay, callback){
printf("execution " + i + " started");
setTimeout(function(){
printf("execution " + i + " finished");
callback();
},delay);
},[1000])());


You can find the code here

No comments:

Post a Comment