1

Let's suppose I have 5 promises (A,B,C,D,E) that I want to fire in parallel. Then I need to wait for any of (A,B) AND any of (C,D) AND E before executing a code. My actual approach was something like:

let promises_AB = new Array();
let promises_CD = new Array();
let promises_E = new Array();
promises_AB.push(promiseA());
promises_AB.push(promiseB());
promises_CD.push(promiseC());
promises_CD.push(promiseD());
promises_E.push(promiseE());

//Promises on top will execute in parallel

try{
  let res = await Promise.any(promises_AB);
  //Do code after getting AB
} catch (e){
  //error handler
}
try{
  let res = await Promise.any(promises_CD);
  //Do code after getting CD
} catch (e){
  //error handler
}
try{
  let res = await Promise.any(promises_E);
  //Do code after getting E
} catch (e){
  //error handler
}

//DO CODE HERE which executes after promises A,B,C,D,E

The problem here is that since promises CD can be faster than AB, since I await first for AB, if an error occurs in CD before AB returns I don't have yet the try/catch for CD and I get a unhandled promise rejection. The only (bad) solution I see is a big try/catch around all three awaits but I wanted to differentiate the error handler per promise group.

  • The [documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) indicates that await pauses the execution. – Nazrul Chowdhury Aug 18 '23 at 22:01

3 Answers3

2

You could use Promise.all() to group the promises:

let promises_AB = Promise.any([promiseA(), promiseB()]);
let promises_CD = Promise.any([promiseC(), promiseD()]);
let promises_E = promiseE(); // No need for Promise.any since it's just one promise

try {
  let [resAB, resCD, resE] = await Promise.all([promises_AB, promises_CD, promises_E]);
  // Do code after getting A or B, C or D, and E
} catch (e) {
  // e is the rejection value of the first rejected promise.
} 

Alternatively, you could use Promise.allSettled() if you want to wait for all the promises to settle, regardless of whether they were fulfilled or rejected. This would allow you to differentiate the error handling per promise group and handle cases where some promises are rejected, and others are fulfilled:

let promises_AB = Promise.any([promiseA(), promiseB()]);
let promises_CD = Promise.any([promiseC(), promiseD()]);
let promises_E = promiseE();

let results = await Promise.allSettled([promises_AB, promises_CD, promises_E]);

if (results[0].status === 'rejected') {
  // Handle error for promises_AB
}
if (results[1].status === 'rejected') {
  // Handle error for promises_CD
}
if (results[2].status === 'rejected') {
  // Handle error for promises_E
}

if (results[0].status === 'fulfilled' && results[1].status === 'fulfilled' &&
    results[2].status === 'fulfilled') {
  // Do code after getting A or B, C or D, and E
}

See recent relevant video Dangers of Promise.all() by t3dotgg@

Sash Sinha
  • 18,743
  • 3
  • 23
  • 40
1

Use Promise.all() to wait for each Promise.any() in parallel.

let [result_AB, result_CD, result_E] = await Promise.all(
    Promise.any(promises_AB), 
    Promise.any(promises_CD), 
    Promise.any(promises_E)
)
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • 1
    Yeah nice, the only "problem" is that Promise.all will return a single error which will be hard to differentiate if it happened in AB, CD or E. Using Promise.allSettled can fix it. – Michele Tuloski Furci Aug 18 '23 at 21:48
  • @MicheleTuloskiFurci Do you actually *need* to differentiate it? Also you already have the same problem where you cannot distinguish between errors from A and B or from C and D… If you really want to, the go-to pattern is `promiseA().catch(cause => { throw new Error("Process A failed", {cause}); })` that you can slap on any promise, including intermediate ones and the ones returned from `Promise.any` or `Promise.all`. – Bergi Aug 18 '23 at 22:47
0

You can "merge" the processing using Promise.all:

const [resAB, resCD, resE] = await Promise.all([
  Promise.any(promises_AB),
  Promise.any(promises_CD),
  Promise.any(promises_E),
]);

// Do after all
Vivick
  • 3,434
  • 2
  • 12
  • 25