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:
wait for it...
appears- the process freezes a little bit because of the loop
dary!
appears
What actually happened:
- the process freezes a little bit because of the loop
wait for it... dary!
appears together
Any idea why such a behavior?
Check by yourself: fiddle.
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:
wait for it...
appears- the process freezes a little bit because of the loop
dary!
appears
What actually happened:
- the process freezes a little bit because of the loop
wait for it... dary!
appears together
Any idea why such a behavior?
Check by yourself: fiddle.
Share Improve this question edited May 23, 2017 at 12:11 CommunityBot 11 silver badge asked Aug 20, 2013 at 16:05 sp00msp00m 48.8k31 gold badges149 silver badges259 bronze badges 4 |5 Answers
Reset to default 17The 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.
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.)
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()
Your code is executing as you expect it. The issue is that the browser won't display your change to the document, until after the javascript is done. The time out is fixing this issue, by breaking the code execution into two separate events. The following code will show that what you expected is happening.
var output = document.getElementById('output');
document.getElementById('go').onclick = function() {
console.log('wait for it...';)
for (var i=0; i<3000000000; i++) {
var unused = i; // don't really care
}
console.log(' dary!');
};
You also need to be careful when using the timeout solution, since execution is no longer synchronous.
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);
output.textContent += ' epic';
};
If you run this version, you will notice that ' epic' is before ' dary'.
The only explanation I see is that the browser refreshes the view after the javascript is executed ? As a proof, this works as expected :
var output = document.getElementById('output');
document.getElementById('go').onclick = function () {
output.textContent += 'wait for it...';
window.setTimeout(count, 100);
};
function count() {
for (var i = 0; i < 3000000000; i++) {
var unused = i; // don't really care
}
output.textContent += ' dary!';
}
looping
is a plain-vanilla process ! – The Dark Knight Commented Aug 20, 2013 at 16:12