Asynchronous loops
Asynchronous loops
Hi,
I often find myself iterating over a very large number of nodes using
jQuery (selecting table rows, list items, etc.), like showing or
hiding items based on a live filter, or recomputing a value inside
every cell of a table.
jQuery each() function is great for this but can sometimes be
frustrating, like when the number of items to iterate on is very
large. In this particular case, the loop can take a long time to
execute (500-1000ms) and this is a problem because JavaScript is
single threaded and takes control of the browser while executing. The
result is that the browser is unresponsive during the process and that
leads to bad user experience.
For the moment, what I do to solve this problem is implement an
asynchronous each loop using setTimeout(). Each iteration of the loop
is actually an asynchronous call sent to setTimeout() in order to let
the browser retake control of the UI between loop iterations. This
gives great results because the browser is still responsive while my
application is processing a large set of DOM nodes.
But another problem comes, letting the browser retake control of the
UI between each loop iteration really expands the full loop time. So
my processing takes around 10 times more time than the synchronous
version, which is not that good either.
Finnally I came with a version of my asynchronous loop that can
execute asynchronously but each asynchronous call actually execute a
limited number of loop iterations. The result is the loop appears
faster because it is executed in many asynchronous batch of
synchronous iterations.
So my point is that this problem is a very hard problem to solve for
web applications displaying large set of data and I do want to share
this code.
So here is the code, I want your opinion on the idea and what I should
do with this. Should I create a new jQuery plugin ? Should I use an
"async" namespace instead of prefixed function names ? Is this a good
candidate for jQuery integration ? Any thoughts appreciated :)
$.eachAsync = function(array, opts)
{
var delay = opts.delay || 0;
var fn = opts.loop || function(){};
var end = opts.end || function(){};
var bulk = Math.max(1, opts.bulk || 1);
if( array.length )
{
var i = 0, l = array.length;
(function(){
for( var b = 0; b < bulk && i < l; ++b, ++i )
{
fn.call(array[i], i, array[i]);
}
if( i < l ){ setTimeout(arguments.callee, delay) }
else{ end() }
})();
}
else
{
var keys = [];
for( var i in array ){ keys.push(i) }
$.eachAsync(keys, delay, function(i, key){ fn.call(array[key], key,
array[key]) })
}
}
$.fn.eachAsync = function(opts)
{
opts.delay = opts.delay || 0;
opts.loop = opts.loop || function(){};
opts.end = opts.end || function(){};
$.eachAsync(this, opts);
return this;
}