Wednesday, January 14th, 2009
Looping through your items without waiting around
Nicholas C. Zakas has taken his long running script post and is now flushing out ways to stop this from happening.
His first post details loops and he offers up a solution to take the synchronicity out of the world:
The secret to unraveling this problem is to evaluate the loop to answer two questions:
- Does the loop have to execute synchronously?
- Does the order in which the loop’s data is processed matter?
If the answer to both of these questions is “no,” then you have some options for splitting up the work done in the loop. The key is to examine the code closely to answer these questions. A typical loop looks like this:
JAVASCRIPT:
for(var i=0; i <items.length; i++){ process(items[i]); }This doesn’t look too bad though may take very long depending on the amount of time necessary to run the
process()function. If there’s no code immediately after the loop that depends on the results of the loop executing, then the answer to the first question is “no.” You can clearly see that each iteration through the loop isn’t dependent on the previous iteration because it’s just dealing with one value at a time, so the answer to the second question is “no.” That means the loop can be split in a way that can free up the browser and avoid long-running script warnings.In Professional JavaScript, Second Edition, I introduce the following function as a way to deal with loops that may take a significant amount of time to execute:
JAVASCRIPT:
function chunk(array, process, context){ var items = array.concat(); //clone the array setTimeout(function(){ var item = items.shift(); process.call(context, item); if (items.length> 0){ setTimeout(arguments.callee, 100); } }, 100); }The
chunk()function is designed to process an array in small chunks (hence the name), and accepts three arguments: a “to do” list of items, the function to process each item, and an optional context variable for setting the value ofthiswithin theprocess()function. A timer is used to delay the processing of each item (100ms in this case, but feel free to alter for your specific use). Each time through, the first item in the array is removed and passed to theprocess()function. If there’s still items left to process, another timer is used to repeat the process.
Looking forward to the rest of the series!












I used similar method to load points for a Google Map.
It’s a good method to avoid browser freeze.
From my investigation setting the timeout to a value smaller than 10ms will cause IE6 to freeze…
Kind of reminds of something :P
Does it really perform that well? It seems very inefficient. I mean, I could see the point of doing it in chunks (of say 100 or so) but that seems to do it for each iteration. Plus why continue to set a timeout? Surely an interval timeout would be better? Then just clear it when you’ve reached the end.
I don’t see any chunk processing there… it’s a one by one thing, so maybe that function should be simply called “asyncProcess”.
Also, there is a tendency on using small timeout values, like 100ms, to have stuff executed “immediately”. Forget about it… go ahead setting 0ms in all cases. It will just work as well, without that 100ms delay. Leave higher timeout values only if you really need them.
Using the same technique in actionscript to process 100mb files with out losing UI responsiveness.
@V1: same here, though not 100 MB. 5 MB of drawing commands, rendered asynchronously. The user can interact with the drawing while it’s still being rendered (most important content rendered first).
Isn’t this what Prototype does with its Function.defer() method?
this is one of the oldest tricks in the book… been using it since at least 1999. Good for the newbies to learn it though.
I think this is what http://www.tumuski.com/code/clumpy/overview/ wraps into a nice library, with calls that resemble the usual flow control statements.
For the jQuery fans out there, I posted a simple jQuery plugin a few weeks ago that adds $.slowEach() and $().slowEach() methods that are similar to this code and are compatible with jQuery’s normal $.each() and $().each():
http://groups.google.com/group/jquery-en/msg/f52a6b8e81af20a7