1

I was under the impression that all DOM manipulations were synchronous.

However, this code is not running as I expect it to.

RecordManager.prototype._instantiateNewRecord = function(node) {
  this.beginLoad(); 
  var new_record = new Record(node.data.fields, this);
  this.endLoad();
};
RecordManager.prototype.beginLoad = function() {
  $(this.loader).removeClass('hidden');
};
RecordManager.prototype.endLoad = function() {
  $(this.loader).addClass('hidden');
};

The Record constructor function is very large and it involves instantiating a whole bunch of Field objects, each of which instantiates some other objects of their own.

This results in a 1-2 second delay and I want to have a loading icon during this delay, so it doesn't just look like the page froze.


I expect the flow of events to be:

  • show loading icon
  • perform record instantiation operation
  • hide loading icon

Except the flow ends up being:

  • perform record instantiation operation
  • show loading icon
  • hide loading icon

So, you never even see the loading icon at all, I only know its loading briefly because the updates in the chrome development tools DOM viewer lag behind a little bit.

Should I be expecting this behavior from my code? If so, why?

Luke
  • 5,567
  • 4
  • 37
  • 66
  • Odd behaviour indeed! From where are you calling `_instantiateNewRecord`? Maybe, if it's from an event handler blocking the rendering queue, you could run it asynchronously? – Kenney Jul 29 '15 at 19:17

2 Answers2

2

Yes, this is to be expected. Although the DOM may have updated, until the browser has a chance to repaint, you won't see it. The repaint will get queued the same way as all other things get queued in the browser (ie it won't happen until the current block of JavaScript has finished executing), though pausing in a debugger will generally allow it to happen.

In your case, you can fix it using setTimeout with an immediate timeout:

RecordManager.prototype._instantiateNewRecord = function(node) {
  this.beginLoad(); 
  setTimeout(function() {
    var new_record = new Record(node.data.fields, this);
    this.endLoad();
  }, 0);
};

This will allow the repaint to happen before executing the next part of your code.

James Thorpe
  • 31,411
  • 5
  • 72
  • 93
  • This is the solution. I actually just found this answer to another question, which goes into a lot more detail on the same topic. http://stackoverflow.com/a/4575011/3581485 – Luke Jul 29 '15 at 19:22
-2

JavaScript is always synchronous. It mimics multi-threaded behavior when it comes to ajax calls and timers, but when the callback gets returned, it will be blocking as usual.

That said, you most likely have a setTimeout in that constructor somewhere (or a method you're using does). Even if it's setTimeout(fnc, 0).

Jeff Howard
  • 17
  • 1
  • 5