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
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
Share Improve this question asked Nov 26, 2015 at 13:58 user5350242user5350242 1- 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 Commented Nov 26, 2015 at 14:00
2 Answers
Reset to default 23Why 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 pletes.
...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 pletion, 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.
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 plete');
}
</script>