Multiple rejects from promises in Promise.all, what exactly happens?

2.7k views Asked by At

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

There are 2 answers

1
Bergi On

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).

0
Julien Barrois On

@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)))
  }