14

I wrote this code which is supposed to say "hi" when I click the "hello" button:

<!DOCTYPE html>

<html>

<head>
<script>
var someLargeNumber = 5000000000;
function hello() {
    document.getElementById('hi').innerHTML = "hi";
    for(var i = 0; i < someLargeNumber; i++) {}
}
</script>
</head>

<body>
<p id="hi"></p>
<input type="button" value="hello" onclick="hello();">
</body>

</html>

It does say hi, but only after the for loop is finished. Why does this happen and how do I fix this?

Thanks

  • 3
    Maybe you could take out the loop. Why is it there? The browser won't (necessarily) re-render the page until the "click" handler is finished. – Pointy Nov 26 '15 at 14:00

2 Answers2

23

Why does this happen...

Because browsers run JavaScript on the main UI thread they use for updating the page, for a variety of reasons. So although you've shown the "hi" text, it doesn't get rendered until the JavaScript code running in response to the event completes.

...and how do I fix this?

Yield back to the browser after adding the text, before doing whatever it is that you're simulating with that loop. setTimeout with a delay of 0 is suitable for many cases:

var someLargeNumber = 5000000000;
function hello() {
    document.getElementById('hi').innerHTML = "hi";
    setTimeout(function() {
        for(var i = 0; i < someLargeNumber; i++) {}
    }, 0);
}

The JavaScript engine works basically in a loop with a task queue (the spec calls them "jobs"). It picks up a job from the queue, runs it to completion, and then looks for the next job. Browsers (usually) update the UI when the engine is between jobs. When an event occurs, a job is queued to call the event handler. The above just moves the loop into a new job it queues via setTimeout, so the browser has a chance after the event job and before the setTimeout job to update the UI.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • [Related question/answers](http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful) dedicated to the `setTimeout(fn,0);` pattern – James Thorpe Nov 26 '15 at 14:12
0

As already answered browser has single UI thread.

Another option is to use Web Worker (provided you are not doing any DOM manipulations in the worker thread), which allows to run operations in an another thread.

Add another js file (say worker.js)

var someLargeNumber = 5000000000;
onmessage = function(e) {
  console.log('Message received from main script');
  for(var i = 0; i < someLargeNumber; i++) {}
  console.log('Posting message back to main script');
  postMessage('done');
}

Back in you main file

<head>
<script>
var myWorker = new Worker("worker.js");
function hello() {
    document.getElementById('hi').innerHTML = "hi";
    myWorker.postMessage('test');
    console.log('Message posted to worker');
}
myWorker.onmessage = function(e) {
  result.textContent = e.data;
  console.log('Worker thread is complete');
}
</script>
prasun
  • 7,073
  • 9
  • 41
  • 59