1

A web-application page contains two big components, which have callbacks called when they are fully rendered.

The problem is to call a function after both of the components are fully rendered.

This can probably be solved by using timeouts and polling some variables, but I guess there should be a better way.

Lets say, I have it like this:

function callMeAfterBothRendered() {...};
libA = libraryA($('.comp1'), {"rendered": function () {...}});
libB = libraryB($('.comp2'), {"rendered": function () {...}});

I've found some material on chained promises, but not quite sure how to apply it to this situation?

One complication is that in reality those libraries are not called directly in the same place (as is simplistically shown above), but in some init-methods deeper in the adapter "classes".

What could be good solution for this, which can also be easily generalized to 3 components?

Roman Susi
  • 4,135
  • 2
  • 32
  • 47
  • 4
    Create two `promises`, each of them should **resolve** once the component is rendered. Then, create an array with the to promises, and pass it to `Promise.all`. The callback will be fired when both are resolved. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all . Live sample here: http://jsfiddle.net/5au8p34v/1/ . Remember to eventually use the Promise polyfill for older IE versions: https://github.com/taylorhakes/promise-polyfill – briosheje Sep 12 '18 at 13:47

3 Answers3

5

Promises would be the best way here.

function callMeAfterBothRendered() {...};
Promise.all([
    new Promise((resolve) => libraryA($('.comp1'), {"rendered": resolve })), 
    new Promise((resolve) => libraryB($('.comp2'), {"rendered": resolve }))
]).then(callMeAfterBothRendered);
Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
  • Will it work in IE11 without transpiling and/or some shim? (IE11 seems not to support Promise?) Ok. Can I use something like described in https://stackoverflow.com/questions/36016327/how-to-make-promises-work-in-ie11 then? – Roman Susi Sep 12 '18 at 13:56
  • @RomanSusi use a polyfill. As far as I remember, you can easily separate the Promise shim from other shims so that you don't need to include all of them, despite including them all would be quite handy. Also, rememeber that ES6 lambdas are not supported in older browsers, so you need to use the regular `function` notation. – briosheje Sep 12 '18 at 14:03
2

Yes, promises can help you here. The first step is to promisify the functions, i.e. create promises that fulfill when the component is rendered.

const libApromise = new Promise(resolve => {
    libraryA($('.comp1'), {"rendered": resolve});
});
const libBpromise = new Promise(resolve => {
    libraryB($('.comp2'), {"rendered": resolve});
});

You can return these promises as values from your init methods or store them on your adapter class instances, pass them around so that you can finally collect them in one array at some place:

const promise = [libApromise, libBpromise];

You now can use Promise.all to create a promise that will fulfill when all of them are fulfilled (i.e when all components are rendered):

Promise.all(promises).then(() => {
    console.log("all done!");
    …
});

This works for arbitrarily many promises in the array.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
2

Using Promise.all, it will wait the end of each of your promise.

const onePromise = () => new Promise(resolve => setTimeout(resolve, 1000));
const anotherPromise = () => new Promise(resolve => setTimeout(resolve, 2000));

const loadPromises = (...args) => {
  return Promise.all(args);
};

loadPromises(onePromise, anotherPromise).then(() => console.log('done'));
Fabien Greard
  • 1,854
  • 1
  • 16
  • 26