Wednesday, January 14th, 2009

Looping through your items without waiting around

Category: Articles, JavaScript

<>p>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:

  1. Does the loop have to execute synchronously?
  2. 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
< view plain text >
  1. for(var i=0; i &lt; items.length; i++){
  2.     process(items[i]);
  3. }

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
< view plain text >
  1. function chunk(array, process, context){
  2.     var items = array.concat();   //clone the array
  3.     setTimeout(function(){
  4.         var item = items.shift();
  5.         process.call(context, item);
  6.  
  7.         if (items.length > 0){
  8.             setTimeout(arguments.callee, 100);
  9.         }
  10.     }, 100);
  11. }

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 of this within the process() 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 the process() 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!

Related Content:

8 Comments »

Comments feed TrackBack URI

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…

Comment by Marin — January 14, 2009

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.

Comment by fredck — January 14, 2009

Using the same technique in actionscript to process 100mb files with out losing UI responsiveness.

Comment by V1 — January 14, 2009

@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).

Comment by Joeri — January 14, 2009

Isn’t this what Prototype does with its Function.defer() method?

Comment by leachryanb — January 14, 2009

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.

Comment by leptons — January 14, 2009

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.

Comment by bugme — January 17, 2009

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

Comment by MG — January 20, 2009

Leave a comment

You must be logged in to post a comment.