Mario
Mario

Reputation: 369

Promise.all API calls, distinguish which threw error and reject only one

Information needed: Using NodeJS framework, Promises.all used with API calls only so asynchronous code

So the bases of my problem lies where I need to create two API calls, let's call them A and B. It is guaranteed that A will either return data or a 404 and B will either return an empty array, data or a 404 (but here the 404 means invalid input, while in API call A, it actually means no resource found). My question is that if A does return a 404, the Promise.all will reject and jump into the catch block as it would normally.

My desired functionality is, if API call A returns a 404, I want API call B to continue and retrieve that data, continuing with my code. Is there a way to distinguish or even separately catch the errors thrown by the two API calls and then carry on if one resolves??

Sample code currently looks like this:

function(param) {
  const A = apiCallA();
  const B = apiCallB();

  return Promise.all([A, B])
  .then( ([resA, resB]) => {
    // If resA is null, catch the error but continue with
    // resB. If this is null also Promise.reject('both API calls failed')

    // else if both resA && resB != null, do some stuff and resolve
  })
  .catch( (err) => {
    // Here is where my question lies. The err object can be either from API call A or B.
    // How would I distinguish this?
  });
}

Upvotes: 0

Views: 1445

Answers (1)

jfriend00
jfriend00

Reputation: 707158

There are several options.

  1. You can catch the error from API call A before the Promise.all() and turn it into a successful request but tagged approrpiately, allowing the Promise.all() to finish.

  2. You can use Promise.allSettled() to get both results, regardless of success or failure.

For the first option, you could put a .catch() handler on apiCallA() that will turn any rejection into a resolve, but will resolve with the error object which you can then later check if you need to:

function(param) {
  const A = apiCallA().catch(err => { return {err} });
  const B = apiCallB();

  return Promise.all([A, B]).then( ([resA, resB]) => {
      // you can check if resA succeeded here
      if (resA instanceof Error) {
          // resA actually failed
          console.log(resA);
      }
  }).catch( (err) => {
      // you would only get here if resB failed
  });
}

For the second option, you use Promise.allSettled():

function(param) {
  const A = apiCallA();
  const B = apiCallB();

  return Promise.allSettled([A, B]).then( ([resA, resB]) => {
    // check both resA.status and resB.status
    if (resA.status === "fulfilled") {
       console.log(resA.value);
    }
    if (res === "fulfilled") {
       console.log(resB.value);
    }
  });
}

Upvotes: 1

Related Questions