Everyone’s written a JavaScript loop that just loops over all the {LIs,
links, divs} on a
page\*,
and it’s pretty standard. Something like
var lis = document.getElementsByTagName(“li”);
for (var i=0; i
This is problematic if there are, say, 2000 LI elements on the page, and
what you’re doing in the loop is semi-intensive (imagine you’re creating
a couple of extra elements to append to each of those LIs, or something
like that). The reason this is a problem is that JavaScript is
single-threaded. A tight loop like this hangs the browser until it’s
finished, you get the “this script has been running for a long time”
dialog, and the user interface doesn’t update while you’re in this kind
of loop. You might think: aha, this will take a long time, so I’ll have
some sort of a progress monitor thing:
var lis = document.getElementsByTagName(“li”);
for (var i=0; i
so you move the bit of work you need to do into a function, and that
function re-schedules itself repeatedly, using `setTimeout`. This time,
your user interface will indeed update, and your progress monitor will
show where you’re up to. There are a couple of caveats with this: it’ll
take a bit longer, and you’re no longer guaranteed to have things
processed in the order you expect, but they’re minor issues. For doing
this in jQuery, a tiny plugin:
jQuery.eachCallback = function(arr, process, callback) {
var cnt = 0;
function work() {
var item = arr[cnt];
process.apply(item);
callback.apply(item, [cnt]);
cnt += 1;
if (cnt < arr.length) {
setTimeout(work, 1);
}
}
setTimeout(work, 1);
};
jQuery.fn.eachCallback = function(process, callback) {
var cnt = 0;
var jq = this;
function work() {
var item = jq.get(cnt);
process.apply(item);
callback.apply(item, [cnt]);
cnt += 1;
if (cnt < jq.length) {
setTimeout(work, 1);
}
}
setTimeout(work, 1);
};
and now you can do
$.eachCallback(someArray, function() {
// “this” is the array item, just like $.each
}, function(loopcount) {
// here you get to do some UI updating
// loopcount is how far into the loop you are
});
$(“li”).eachCallback(function() {
// do something to this
}, function(loopcount) {
// update the UI
});
Not always a useful technique, but when you need it, you need it.
Not blocking the UI in tight JavaScript loops
I'm currently available for hire, to help you plan, architect, and build new systems, and for technical writing
and articles. You can take a look at some projects I've worked on and
some of my writing. If you'd like to talk about your upcoming project,
do get in touch.