0

Imagine that we have a Sequence widget which loads Element widgets and loads some config for each of them (through loadConfig()) .Schematic view is described on the image below:

enter image description here

The problem is that every approach I tried does the same "bad" thing: it freezes everything until all Elements are loaded.

array.forEach(elements, function(element) {
  element.loadConfig();
});

or

var counter = 0;
var loadConfig = function(element) {
  element.loadConfig()
  if (++counter <= elements.length - 1) {
    loadConfig(elements(counter));
  }
};
loadConfig(0);

Is there any way to load and show elements one by one, instead of trying to load everything at once? JavaScript doesn't have multi-threading.. so I am really running out of ideas.

EDIT: Tried to use setTimeout() as suggested in this great answer Why is setTimeout(fn, 0) sometimes useful? But this doesn't solve the problem. Looks like Dojo has something that prevents widgets to be rendered one by one - it loads while all data is loaded and then load them all together.

EDIT2: This is how setTimeout() was tried to be used:

array.forEach(elements, function(element) {
  setTimeout(function() {
    element.loadConfig();
  }, 0);
});

EDIT3: Here is full code what is going on. The hierarchy is: TestSequence > Test > ElementsGroup > Element.

// Inside TestSequence widget:
...
var config = [someConfig1, someConfig2, ... someCondigN]

array.forEach(sequenceConfig, function(sequenceConfigItem, i) {
  require(['some_path/' + sequenceConfig.type], function(cls) {
    var test = new cls();
    test.set('config', sequenceConfigItem);
  });
}, this);
...
// Inside Test widget
...
_setConfigAttr: function(testConfig) {
  elementsGroup.set('config', testConfig);
},
...
// Inside ElementsGroup widget
...
_setConfigAttr: function(elementsGroupConfig) {
  array.forEach(config, function(elemenstGroupConfigItem, i) {
    require(['some_path/' + elementsGroupConfigItem.type], function(cls) {
      var element = new cls(); // <--- removing this solves the problem
      element.set('config', elementsGroupConfigItem); 
    });
  }, this);
},
...
// Inside Element widget
...
_setConfigAttr: function(testConfig) {
  // apply simple DOM-stuff - set name, description, etc.
},
...
0leg
  • 13,464
  • 16
  • 70
  • 94
  • What's in `loadConfig`? Ordinarily creating a single widget shouldn't be enough to visibly hang the UI thread... – Ken Franqueiro Oct 15 '15 at 12:38
  • @KenFranqueiro Sequence holds data, which has to be passed to each Element (it is handled then by Element - loading other data and widgets inside, which takes time). Lets say we have 15 Elements. The problem is that the browser waits for all 15 to load their stuff inside.. and it takes time during which the page is "frozen" (even the mighty setTimer()) doesn't help. – 0leg Oct 15 '15 at 12:40
  • 1
    Well, you haven't posted exactly what you tried with `setTimeout` so it's impossible to diagnose while it's still waiting for all 15 at once. But if the UI thread is hanging while data is *loading*, then it sounds like you have a synchronous XHR somewhere, which is a Bad Idea. – Ken Franqueiro Oct 15 '15 at 12:44
  • @KenFranqueiro Added example of `setTimeout` usage in EDIT2. Thought this would work as some kind of multi-thread solution. If I have 15 Elements, the loop would quickly run over all of them and create 15 threads. Once every thread (Element's business) is done - it would just notify Sequence via `on.emit`. However, looks like the problem lies in synchronous XHR, as you suggested. Going to look for it now. – 0leg Oct 15 '15 at 12:49
  • 1
    One thing that that linked setTimeout answer seems to neglect to correct from the unfortunately "accepted" one above it is that there is *one* UI thread. There is no multithreading in this approach, only queueing. Calling `setTimeout` with no delay should break up the action to give the UI thread a tiny bit of breathing room between each execution, but otherwise you're still performing one call immediately after the other. Meanwhile, I still suspect that something *inside* `loadConfig` is what's actually causing the hanging, so we need to know what's in there. – Ken Franqueiro Oct 15 '15 at 12:54
  • @KenFranqueiro Unfortunately, I can not post the `loadConfig` since it is huge. However I have localised the problem to one string and looks like it is `lang.hitch`. – 0leg Oct 15 '15 at 13:06
  • `lang.hitch` is simply used to preserve execution context when another function is called. The function being called is what's important, not `hitch` itself. Without seeing what's actually going on, it's impossible to accurately diagnose this problem. Sync XHR is still my first guess. – Ken Franqueiro Oct 15 '15 at 14:35
  • @KenFranqueiro Added EDIT3 with full explanation. Have 4 layers of widgets. Have marked the line removing which - removes the UI-thread freeze completely. Just imagine what happens when there are 50 Steps with ElementGroups with Elements inside. Browser tried to load all of them non-stop. – 0leg Oct 15 '15 at 15:05

1 Answers1

0

The solution was to use setTimeout along with recursive function, like this:

var counter = 0;

recursiveFunc: function(element) {
  setTimeout(function() {
    // Do stuff for element[counter]

    if (++counter < numberOfElements) {
      recursiveFunc(element);
    }
  }, 0);
}

recursiveFunc(element[counter])

The UI thread would still be buggy, but at least it is not frozen. This solution was picked as a fast one. For a long-one it was decided to optimize the code to get rid of sync XHR request.

0leg
  • 13,464
  • 16
  • 70
  • 94