Audun Olsen
Audun Olsen

Reputation: 628

Begin fetch on event, but wait until itself and another promise settles — JavaScript

I’m developing an organisational chart. I’m looking for tips on restructuring how I handle two es6-promises for a minor performance gain.

On the click event, I do 3 things sequentially:

  1. Play first animation (promise)
  2. Fetch all data (promise)
  3. Play last animation

Ideally, I would like:

  1. Begin fetching data
  2. Play first animation
  3. Play last animation (requires the fetched data, but can’t play before the first animation has ended)

Here is the code in question:

/* Click happened! */

orgWrap.shiftRow({ row: clickY, from: clickX, to: focusX }).then(() => {
    /* Shiftrow is a promise triggering an animation,
       it resolved on 'transitionend' */

    fetch(`api/org/${target.id}/1`).then(result =>  {
        /* a function placing result in the DOM runs
           After data finished loading */

        orgWrap.populateRow({ data: result.children, row: clickY+1 });
        /* The animation which plays last, when shiftRow and fetch has settled */
  });

});

As it stands now, there is an occasional unnecessary delay between shiftRow and populateRow because it fetches all data after the first animation is done and then runs the final animation, instead it should begin to fetch data on the initial click. Any tips on how I can refactor and better utilise promises and the power of async? I prefer to keep the functions which are promises to stay promises, the rest I am willing to change. Any input is greatly appreciated.

Upvotes: 0

Views: 264

Answers (3)

Bergi
Bergi

Reputation: 664548

You'll want to use Promise.all for this:

/* Click happened! */

Promise.all([
  orgWrap.shiftRow({ row: clickY, from: clickX, to: focusX }), // Shiftrow is a promise triggering an animation, it resolved on 'transitionend'
  fetch(`api/org/${target.id}/1`),
]).then(([animationResult, fetchResult]) => {
  /* a function placing result in the DOM runs after data finished loading */

  return orgWrap.populateRow({ data: result.children, row: clickY+1 }); // The animation which plays last, when shiftRow and fetch has settled
});

Also make sure to chain your promise callbacks instead of unnecessarily nesting them.

Upvotes: 1

zero298
zero298

Reputation: 26878

Wrap the fetch and begin animation in a Promise.all() and run the end animation after the Promise.all resolves.

So:

async function animate() {
  // some animation logic
}

Promise
  .all([
    animate(), // Beginning animation
    fetch("some.html")
  ])
  .then([, neededFetchData] => {
    return animate(neededFetchData);
  });

Upvotes: 1

Georgy
Georgy

Reputation: 2462

You can use Promise.all() for the first two promises, then for the third one. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

Upvotes: 2

Related Questions