Sam H
Sam H

Reputation: 561

How to wait for 2 of Promises in Promise.all to finish and then do another task

I'm currently developing a program where I need to use Promise.all to run a few functions at the same time. But before I can continue with the task, I need ONLY 2 of the promises to finish and then run the .then(), how would I go about doing this?

Example:

await Promise.all([Task1(),Task2(),Task3(),Task4(),Task5()]);

I need it to continue the code when only (for example) Task1 and Task 4 have finished.

I have tried to experiment by using a while loop waiting for Task1 and Task2 finish by settings variables on the finish, but that. doesn't work at all.

Upvotes: 0

Views: 1372

Answers (5)

T.J. Crowder
T.J. Crowder

Reputation: 1074058

In a comment you seem to have said you specifically know that in advance which two are more urgent than the rest (and that in this example, it's Task1 and Task4).

Then just use Promise.all twice:

const allResults = Promise.all([
    Promise.all([Task1(), Task4()])
    .then(([result1, result4]) => {
        // Those two are done, do what you like with `result1` and `result4`...
        return [result1, result4];
    }),
    Task2(),
    Task3(),
    Task5()
])
.then(([[result1, result4], result2, result3, result5]) => {
    // All five are done now, let's put them in order
    return [result1, result2, result3, result4, result5];
})
.then(/*...*/)
.catch(/*...*/);

In there, I've preserved the overall 1, 2, 3, 4, 5 order in the outer chain by remapping the order in the overall then handler.


Originally, I assumed you wanted to wait until any two have finished, rather than a specific two. There's no built-in for that, but it's easy enough to write:

function enough(promises, min) {
  if (typeof min !== "number") {
    return Promise.all(promises);
  }
  let counter = 0;
  const results = [];
  return new Promise((resolve, reject) => {
    let index = 0;
    for (const promise of promises) {
      let position = index++;
      promise.then(
        result => {
          results[position] = result;
          if (++counter >= min) {
            resolve(results);
          }
        },
        reject
      );
    }
  });
}

Live Example:

function enough(promises, min) {
  if (typeof min !== "number") {
    return Promise.all(promises);
  }
  let counter = 0;
  const results = [];
  return new Promise((resolve, reject) => {
    let index = 0;
    for (const promise of promises) {
      let position = index++;
      promise.then(
        result => {
          results[position] = result;
          if (++counter >= min) {
            resolve(results);
          }
        },
        reject
      );
    }
  });
}

const delay = (ms, ...args) => new Promise(resolve => setTimeout(resolve, ms, ...args));
const rnd = () => Math.random() * 1000;

enough(
  [
    delay(rnd(), "a"),
    delay(rnd(), "b"),
    delay(rnd(), "c"),
    delay(rnd(), "d"),
    delay(rnd(), "e")
  ],
  2
)
.then(results => {
  console.log(results);
})
.catch(error => {
  console.error(error);
});

Upvotes: 5

jo_va
jo_va

Reputation: 13964

Promise.all() does not run your tasks simultaneously, it only awaits all Promises to resolve before resolving the returned Promise.

Your tasks will be run as soon as you create each Promise.

If you want to wait after specific tasks, only include those tasks in Promise.all:

const tasks = [Task2(), Task3(), Task5()];
const result1 = await Promise.all([Task1(), Task4()]);
// Task1 and Task4 are done here
const result2 = await Promise.all(tasks);
// All remaining tasks are done here

Upvotes: 0

mbojko
mbojko

Reputation: 14679

OK, as I understand you, you want to do something like

const importantOnes = [Task1(), Task2()];
const remainingOnes = [Task3(), Task4(), Task5()];
const priorityPromise = Promise.all(importantOnes);

priorityPromise.then(doPriorityStuff);
Promise.all([priorityPromise, ...remainingOnes]).then(processTheCompleteData);

Upvotes: 0

Moad Ennagi
Moad Ennagi

Reputation: 1073

I've seen tricks like this : Promise.all(promises.map(p => p.catch(() => undefined))); Quite unsafe though.
Original answer : here

Upvotes: 0

31piy
31piy

Reputation: 23859

One way to do it is by constructing a new array of the randomized promises, and then wait for those only:

let array = [Task1(),Task2(),Task3(),Task4(),Task5()];

// Select any two promises after running the randomization logic
let promises = Promise.all[array[1], array[3]];

promises
  .then(() => {
    // Do stuff here
  });

Upvotes: 1

Related Questions