Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A 3,000,000,000 iterations loop behaves weirdly

While trying to answer this question, I met a weird behavior (which isn't the same: his is due to too few iterations, mine to too much):

HTML:

<button id="go">it will be legend...</button>
<div id="output"></div>

JS:

var output = document.getElementById('output');
document.getElementById('go').onclick = function() {
    output.textContent += 'wait for it...';
    for (var i=0; i<3000000000; i++) {
        var unused = i; // don't really care
    }
    output.textContent += ' dary!';
};

The loop takes few seconds to execute, because of its 3,000,000,000 iterations.

Once the button is clicked, what I expected:

  1. wait for it... appears
  2. the process freezes a little bit because of the loop
  3. dary! appears

What actually happened:

  1. the process freezes a little bit because of the loop
  2. wait for it... dary! appears together

Any idea why such a behavior?

Check by yourself: fiddle.

like image 597
sp00m Avatar asked Aug 20 '13 16:08

sp00m


3 Answers

The reason is the function, as a whole, is executing synchronously. By the time you set the output to wait for it..., it enters the long-running loop and hog the thread. If you wrap the rest in a timeout, the first text will appear as normal.

var output = document.getElementById('output');
document.getElementById('go').onclick = function() {
    output.textContent += 'wait for it...';
    window.setTimeout(function() {
    for (var i=0; i<3000000000; i++) {
        var unused = i; // don't really care
    }
    output.textContent += ' dary!';
    }, 0);
};

Note that it will still freeze the UI while processing.

Edit: Using 0 as the delay value in Chrome works, but it does not in latest Firefox and IE 10. Changing the value to 10 works in both cases.

like image 87
Simon Belanger Avatar answered Nov 06 '22 15:11

Simon Belanger


Javascript is pretty much single-threaded. If you're running code, the page is non-responsive and will not be updated until your code has completed. (Note that this is implementation specific, but this is how all browsers do it today.)

like image 29
Dark Falcon Avatar answered Nov 06 '22 15:11

Dark Falcon


Dark Falcon and Simon Belanger have provided explanations for the cause; this post discusses a different solution. However, this solution is definitely NOT appropriate for a 3-billion iteration loop as it is too slow by comparison.

According to this SO post by user Cocco, using setTimeout is less optimal than requestAnimationFrame for this purpose. So, here's how to use requestAnimationFrame:

jsFiddle example

$(document).ready(function() {
    var W = window,
        D = W.document,
        i = 0,
        x = 0,
        output = D.getElementById('output');

    function b() {
        if (x == 0) {
            output.textContent = 'wait for it...';
            x++;
        }
        i++;
        if (i < 300) {
            //if (i > 20) output.textContent = i;
            requestAnimationFrame(b);
        } else {
            D.body.style.cursor = 'default';
            output.textContent += ' dary!';
        }
    }

    function start() {
        console.log(D)
        D.body.style.cursor = 'wait';
        b();
    }
    D.getElementById('go').onclick = start;
}); //END $(document).ready()
like image 2
cssyphus Avatar answered Nov 06 '22 14:11

cssyphus