Cazineer
Cazineer

Reputation: 2405

What happens when a Promise in a Promise.all, in a then chain fails

When there is a .then chain that includes a Promise.all such as the following example, do the errors get caught in the final .catch block for the parent .then chain or do they require their own error handling?

.then(([user, booking]) => {
  const payment = { }

  return Promise.all([
    paymentService(payment),
    Promise.resolve(user),
    Promise.resolve(booking)
  ])
})
.then(([paid, user, booking]) => {
  return Promise.all([
    repo.makeBooking(user, booking),
    Promise.resolve(paid),
    Promise.resolve(user)
  ])
})
.catch((err) => {
    //Error handling here
})

Upvotes: 0

Views: 758

Answers (3)

jherax
jherax

Reputation: 5267

As you are returning a promise inside each .then(), the errors are chained to the main flow, so in your example, the .catch method will catch all errors.

Consider this example:

Promise.resolve(1)
  .then((n) => {
    return Promise.all([
      Promise.resolve(`a-${n}`),
      Promise.reject(Error('Failed in Promise.all')), // comment for success
    ]);
  })
  .then(([a, b]) => {
    const msg = `Responses: [${a}, ${b}]`;
    console.log(msg);
    return msg;
  })
  .catch((error) => { throw error; });

Here the inner Promise.all() has a reject that is caught by the parent flow.

Read some about chaining promises here: #2 Nested Promises

Upvotes: 1

intentionally-left-nil
intentionally-left-nil

Reputation: 8284

Simply put, .catch() gets the value of a promise that is rejected. Promise.prototype.catch(myHandler) is the same thing as calling Promise.prototype.then(undefined, myHandler). So it

For promises that are chained with .then(), what that means is that if the previous promise fails, then the subsequent rejected callback is called.

So if you have a the following code:

Promise.resolve().then((resolve, reject) => {
  console.log('this is the first promise');
  reject();
}).then(() => {
  console.log('this is the second promise');
  reject();
}).catch((value) => {
  console.log('this is the third promise')
});

The output is:

this is the first promise

this is the third promise

The reason for that is follows: Promise.resolve() creates a promise. Let's number that as promise #1.

Note that .then returns a new promise equal to the return of the inner promise. So by calling .then() on line 1, we create promise #2. The second .then() call creates promise #3.

Okay. Now that's set up, Promise #1 is already resolved, so it calls into the success case. That's why 'this is the first promise' prints. We then reject promise #2 inside the handler.

Promise #2 has one handler on it (with the body 'this is the second promise'). Since Promise #2 is rejected, this handler does not run. ** The way chained promises works means that this also causes Promise #3 to be rejected **. Therefore when we get to the catch() handler, it runs the this is the third promise example.

You can read more here for more examples: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch

Upvotes: 1

mdziekon
mdziekon

Reputation: 3627

Yes, they are caught by the last (in your example) catch().


You could also insert another catch() just after the then() with Promise.all(), and in that case both catch()-es would be called. The only catch with that is that you would have to manually "rethrow" the error from the first catch():

.then(([user, booking]) => {
  const payment = { }

  return Promise.all([
    paymentService(payment),
    Promise.resolve(user),
    Promise.resolve(booking)
  ])
})
.catch((error) => {
  // Error handling logic...

  // Rethrow the error to allow the second catch to... catch it
  throw error;
})
.then(([paid, user, booking]) => {
  return Promise.all([
    repo.makeBooking(user, booking),
    Promise.resolve(paid),
    Promise.resolve(user)
  ])
})
.catch((err) => {
    //Error handling here
})

Upvotes: 1

Related Questions