How to stop intense Javascript loop from freezing the browser

JavascriptJqueryPerformanceLoops

Javascript Problem Overview


I'm using Javascript to parse an XML file with about 3,500 elements. I'm using a jQuery "each" function, but I could use any form of loop.
The problem is that the browser freezes for a few seconds while the loop executes. What's the best way to stop freezing the browser without slowing the code down too much?

$(xmlDoc).find("Object").each(function() {
    //Processing here
});

Javascript Solutions


Solution 1 - Javascript

I would ditch the "each" function in favour of a for loop since it is faster. I would also add some waits using the "setTimeout" but only every so often and only if needed. You don't want to wait for 5ms each time because then processing 3500 records would take approx 17.5 seconds.

Below is an example using a for loop that processes 100 records (you can tweak that) at 5 ms intervals which gives a 175 ms overhead.

var xmlElements = $(xmlDoc).find('Object');
var length = xmlElements.length;
var index = 0;
var process = function() {
  for (; index < length; index++) {
    var toProcess = xmlElements[index];
    // Perform xml processing
    if (index + 1 < length && index % 100 == 0) {
        setTimeout(process, 5);
    }
  }
};
process();

I would also benchmark the different parts of the xml processing to see if there is a bottleneck somewhere that may be fixed. You can benchmark in firefox using firebug's profiler and by writing out to the console like this:

// start benchmark
var t = new Date();
// some xml processing
console.log("Time to process: " + new Date() - t + "ms");

Hope this helps.

Solution 2 - Javascript

Set a timeOut between processing to prevent the loop cycle from eating up all the browser resources. In total it would only take a few seconds to process and loop through everything, not unreasonable for 3,500 elements.

var xmlElements = $(xmlDoc).find('Object');

var processing = function() {
  var element = xmlElements.shift();

  //process element;

  if (xmlElements.length > 0) {
    setTimeout(processing, 5);
  }
}

processing();

Solution 3 - Javascript

I'd consider converting the 3500 elements from xml to JSON serverside or even better upload it to server converted, so that it's native to JS from the getgo.

This would minimize your load and prolly make the file size smaller too.

Solution 4 - Javascript

you can setTimeout() with duration of ZERO and it will yield as desired

Solution 5 - Javascript

Long loops without freezing the browser is possible with the Turboid framework. With it, you can write code like:

loop(function(){  
        // Do something...  
}, number_of_iterations, number_of_milliseconds);

More details in this turboid.net article: Real loops in Javascript

Solution 6 - Javascript

Javascript is single-threaded, so aside from setTimeout, there's not much you can do. If using Google Gears is an option for your site, they provide the ability to run javascript in a true background thread.

Solution 7 - Javascript

You could use the HTML5 workers API, but that will only work on Firefox 3.1 and Safari 4 betas atm.

Solution 8 - Javascript

I had the same problem which was happening when user refreshed the page successively. The reason was two nested for loops which happened more than 52000 times. This problem was harsher in Firefox 24 than in Chrome 29 since Firefox would crash sooner (around 2000 ms sooner than Chrome). What I simply did and it worked was that I user "for" loops instead of each and then I refactored the code so that I divided the whole loop array to 4 separated calls and then merged the result into one. This solution has proven that it has worked.

Something like this:

var entittiesToLoop = ["..."]; // Mainly a big array
   loopForSubset(0, firstInterval);
   loopForSubset(firstInterval, secondInterval);
    ...

var loopForSubset = function (startIndex, endIndex) {
    for (var i=startIndex; i < endIndex; i++) {
            //Do your stuff as usual here
    }
}

The other solution which also worked for me was the same solution implemented with Worker APIs from HTML5. Use the same concept in workers as they avoid your browser to be frozen because they run in the background of your main thread. If just applying this with Workers API did not work, place each of instances of loopForSubset in different workers and merge the result inside the main caller of Worker.

I mean this might not be perfect but this has worked. I can help with more real code chunks, if someone still thinks this might suite them.

Solution 9 - Javascript

You could try shortening the code by

   $(xmlDoc).find("Object").each(function(arg1) {
    (function(arg1_received) {
                setTimeout(function(arg1_received_reached) {
        
                    //your stuff with the arg1_received_reached goes here 
        
                }(arg1_received), 0)
            })(arg1)
}(this));

This won't harm you much ;)

Solution 10 - Javascript

As a modification of @tj111 answer the full usable code

    //add pop and shift functions to jQuery library. put in somewhere in your code.
    //pop function is now used here but you can use it in other parts of your code.
    (function( $ ) {
        $.fn.pop = function() {
            var top = this.get(-1);
            this.splice(this.length-1,1);
            return top;
        };
    
        $.fn.shift = function() {
            var bottom = this.get(0);
            this.splice(0,1);
            return bottom;
        };
    })( jQuery );
    

//the core of the code:
    var $div = $('body').find('div');//.each();
    var s= $div.length;
    var mIndex = 0;
    var process = function() {
    	var $div = $div.first();			
    //here your own code.
    
    //progress bar:
    	mIndex++;
    // e.g.:	progressBar(mIndex/s*100.,$pb0);
    
    //start new iteration.
    	$div.shift();
    	if($div.size()>0){
    		setTimeout(process, 5);
    	} else {
    //when calculations are finished.
    		console.log('finished');
    	}
    }
    process();

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChris BView Question on Stackoverflow
Solution 1 - JavascriptHelgiView Answer on Stackoverflow
Solution 2 - JavascriptTJ LView Answer on Stackoverflow
Solution 3 - JavascriptMikko TapionlinnaView Answer on Stackoverflow
Solution 4 - JavascriptScott EverndenView Answer on Stackoverflow
Solution 5 - Javascriptsharp solutionView Answer on Stackoverflow
Solution 6 - JavascriptGabe MoothartView Answer on Stackoverflow
Solution 7 - JavascriptolliejView Answer on Stackoverflow
Solution 8 - JavascriptFidEliOView Answer on Stackoverflow
Solution 9 - JavascriptLINTUismView Answer on Stackoverflow
Solution 10 - JavascriptVyacheslavView Answer on Stackoverflow