4
votes

Promise.all([iterable]) is all or nothing, meaning that the promise it returns resolves when every promise in the iterable resolves, or rejects as soon as one of the promises rejects, with the reason of the first promise that rejects (doc).

But what happens if multiple promises of the iterable reject?

In VSCode, I tried the following example, and purposely made both foo() and bar() promises fail. When I debug in VSCode I get an error on * catch(err => Promise.reject('error query bar()'))* saying Exception has occurred and I don't understand why.

I think it is because when I call Promise.reject the Promise.all has already received a reject from the foo function that also fails, but it is not clear what is happening.

If I disable the "Uncaught Exceptions" breakpoint in the debugging options, the exception doesn't show up anymore.

What exactly is happening here?

function foo() {
  return pool.query('insert_test into test (value) values (20)')
    .then(() => client.query('insert into test (value) values (21)'))
    .catch(err => Promise.reject('error query bar()'))
}

function bar() {
  return pool.query('insert_test into test (value) values (20)')
    .then(() => client.query('insert into test (value) values (21)'))
    .catch(err => Promise.reject('error query bar()'))
 }

 Promise.all([foo(), bar()])
   .then(results => {
     console.log(results)
   })
   .catch(err => {
     console.log(err)
   });

This is a screenshot of what I see with I have Uncaught Exceptions enabled. enter image description here

2
You don't need to call Promise.reject() in catch, since catch is a Promise function (See here: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…). Further, you do not have to catch errors in foo() nor in bar(), the catch-Function in Promise.all handles all rejections! - noChance
@noChance I think he did that to distinguish errors from foo from errors from bar - Bergi
@noChance thank you for your answer. Yeah, I wanted to distinguish bar's error from foo's error. But more importantly, if I don't call Promise.reject, it won't show up in the Promise.all catch, but in the then, right? - smellyarmpits
I am considering the case where I would substitute .catch(err=>Promise.reject()) with .catch(err=>{}) - smellyarmpits
@MarcoGalassi No, the the catch function of the Promise.all handles all errors. As you stated: Promise.all([iterable]) is all or nothing . So you can queue as many promises as you want in foo() or bar(), but any error will be handled by the outer Promise.all.catch-function. - noChance

2 Answers

4
votes

But what happens if multiple promises of the iterable reject?

The first rejection wins and gets its reason to reject the Promise.all promise. "First" in here means "happening earliest", if the promises already were rejected when Promise.all visits them iteration order is important.

The subsequent rejections will be ignored.

If I disable the "Uncaught Exceptions" breakpoint in the debugging options, the exception doesn't show up anymore.

That's weird. An ignored rejection should not cause an unhandled rejection (or uncaught exception).

-1
votes

@smellyarmpits If you want to prevent uncaught exception for remaining promises that gets rejected after the first promise is rejected and therefore after the Promise.all is rejected, to can try a custom implementation of Promise.all that wraps the original one like this (typescript) :

  const promiseAll = async (promises: Promise<any>[]) => {
    let oneHasRejected = false
    const onReject = (err: any) => {
      if (oneHasRejected) {
        oneHasRejected = true
        return Promise.reject(err)
      } else {
        return Promise.resolve()
      }
    }
    return Promise.all(promises.map((p) => p.then((r) => r, onReject)))
  }