1

My problem is more complex, but I've arrived at this simple & concise example:

HTML:

First: <span id="first"></span> <br/> 
Then: <span id="second"></span> <br/> 
And then: <span id="third"></span> <br/> 
And then: <span id="fourth"></span> <br/> 
And then: <span id="fifth"></span>

JS (+jQuery (if matters)):

$(document).ready(function() {
  doStuff();
});

function doStuff() {
  $("#first").text(new Date().getSeconds());
  var i = 1;
  while (i < 1000000000) {
    i++;
  }
  $("#second").text(new Date().getSeconds());
  var i = 1;
  while (i < 1000000000) {
    i++;
  }
  $("#third").text(new Date().getSeconds());
  var i = 1;
  while (i < 1000000000) {
    i++;
  }
  $("#fourth").text(new Date().getSeconds());
  var i = 1;
  while (i < 1000000000) {
    i++;
  }
  $("#fifth").text(new Date().getSeconds());
}

And fiddle.

From this example, my intuition tells me the $("#first") should receive its text value way before the $("#fifth"). However, they all appear at the same time. And no, it's not a matter of JS running really fast, as you can see in the example that the $("#first") has its value ~2 seconds lower than the $("#fifth").

Why is this happening (and what!), and what solution(s) exist?

iuliu.net
  • 6,666
  • 6
  • 46
  • 69
  • thats my output: First: 42 Then: 42 And then: 43 And then: 43 And then: 44, whats mysterious i cant get u bro – Ayan Jul 26 '16 at 14:22
  • javascript is single thread language. Your while loops are freezing the UI. Webworkers could be a solution but in fact you cannot access DOM from a worker so it really depends what is your real use case. Using timeout could be an other solution but it would just be a workaround – A. Wolff Jul 26 '16 at 14:24
  • I suspect the problem you are seeing is a browser specific bug. With chrome I get First: 38 Then: 39 And then: 40 And then: 41 And then: 43 – bhspencer Jul 26 '16 at 14:27
  • @bhspencer But you get them all at once, not one after other. That's all the question here, if i'm correct: `$("#first") should get its text value way before the $("#fifth")` – A. Wolff Jul 26 '16 at 14:28
  • Operators in Javascript be proccess same time not like PHP – kollein Jul 26 '16 at 14:29
  • Yes you get them all at once. That is to be expected. The DOM is not reflowed until the end of the JS block. – bhspencer Jul 26 '16 at 14:29
  • If you need the DOM to be updated more frequently you need to devide your code into multiple JS blocks. you can do this with timeouts. The function you pass to each timeout would run as a separate JS block and the DOM would be updated between them. – bhspencer Jul 26 '16 at 14:30
  • You should read about the JavaScript Event Loop. https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop – bhspencer Jul 26 '16 at 14:40
  • If any of the answers helped you, please mark it as the correct one - this may help other guys checking this question in the future. Thx. – Anton Jul 27 '16 at 14:28

2 Answers2

0

javascript blocks page rendering, so until all the javascript block is done, it will not render the html output.

Jordi Flores
  • 2,080
  • 10
  • 16
  • true, but that JS needs to write in the DOM, so the JS is blocking the painting that's happening inside the script, until it finishes. – Jordi Flores Aug 03 '16 at 09:59
0

JavaScript blocks the UI before it is finished. It is supposed to be a single-threaded language, however (as explained in this post) it is not that simple.

The solution to your problem is to use setTimeout. See the updated simplified fiddle (the example is not suitable for copy paste but I believe it is sufficient to present the idea):

$(document).ready(function() {
  doStuff();
});

function doStuff() {
  $("#first").text(new Date().getSeconds());
  var i = 1;

  setTimeout(function() {
    while (i < 1000000000) {
      i++;
    }
    $("#second").text(new Date().getSeconds());
  }, 0);
}

The setTimeout ensures that the doStuff functions finishes, the browser then processes the DOM changes and then the other function (passed to the setTimeout method) is executed.

A nice and succinct explanation of why things happen this way can be found here (thank you @bhspencer for providing the link). Simply and shortly: While JS is processing the doStuff method, the UI is blocked (due to its single-threaded nature). The call to setTimeout with second parameter set to 0 milliseconds puts (immediately) into the processing queue another piece of code (first parameter of setTimeout). Then the doStuff method finishes. JS then processes next item from the queue - which is apparently the UI update. Again, when UI update is finished, next item in the queue is what was added there by the setTimout call.

Community
  • 1
  • 1
Anton
  • 2,458
  • 2
  • 18
  • 30
  • 1
    Some explanation of why setTimeout ensures that the doStull finishes would improve this answer. Some mention of the javascript event loop might help. https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop – bhspencer Jul 26 '16 at 14:38