Tuesday 18 December 2012

Processing large arrays with little latency

Recently I was answering a question on StackOverflow about how to split up the processing of a very large array of objects. The question author basically wanted to reduce the perceived latency in the processing performance (processing 4000 rows locked the browser for a few seconds).

I came up with a simple enough solution, write a new "each()" method that allowed you to use setTimeout to batch process the array in chunks.

Here is the function I ended up with at the end:

    function _each(arrfn /*fn(el,i)*/ limit /*items per iteration*/ callback{
        var count 0,
            len arr.length;

        function run({
            var limit;
            while (d-- && len >= count {
                fn(arr[count]count++);
            }
            if (len count{
                setTimeout(run1);
            else {
                if (typeof callback === "function"{
                    callback();
                }
            }
        }
        run();
    }

As you can see the code is pretty basic. The user passes in the array, the function to process each index, and the limit for items per pass. Optionally the user can supply a callback function to execute once the entire array is processed.

Why does it work? Well the JavaScript engine executes on a single thread, however the timers queue up and allow break points in the execution for other processes to run during these breaks. Even though only a single process is executing at one point in time, the perceived performance increase is very noticeable to the end user.

Check out the fiddle below:


And there you have it! Processing thousands of items in an array in chunks while not locking the browser.

Now, after all of this is said and done it should be mentioned that processing such a large array is a bad idea to begin with, and if you are attempting to do so then re-factoring your code to avoid this is recommended. Loops should be doing as little processing as possible each iteration, and the overall number of iterations should also be kept to a minimal. I wrote this code for scenarios where you are left with little to no other options.

I recommend reading Zakas’ JavaScript performance tips for anyone concerned about JavaScript performance (Which we all should be).

1 comment:

  1. Nice tip! It is to be noted that node.js has `process.nextTick` for this exact purpose: doing something on the next loop around.

    ReplyDelete