-1

What is the proper way to handle errors from nested promises?

Setup

new Promise((resolve, reject) => {
  // literally anything
  httpCommonServiceAPI
    .getDataWithoutParams(url)
    .then((response) => resolve(response.data))
    .catch((error) => reject(error))
});

httpCommonServiceAPI.getDataWithoutParams = (url) => 
  new Promise((resolve, reject) => 
    Axios.get(url)
         .then((response) => resolve(response.data))
         .catch((error) => reject(error));

I know if I replace the second promise with a try/catch block I can just call throw error but I am trying to accomplish this with nested promises. Currently, it just passes the error in the lower promise catch handler to the response of the first promise and does not treat the lower error as an actual error in the outer promise.

Note: Going off what Codebling said about the unclear behavior. The first block and the second are in 2 different files and are loaded in before they are called so both are accessible. Secondly, the first promise at the top is doing stuff and then it calls a method that is a promise and it wants to handle the errors if any. In the method getDataWithoutParams() we have it as a promise that is making an Axios request.

Note 2: Some have said that the Axios request itself is a promise, I am aware. But what I am asking is when the Axios request sends back an error we can capture that with the .catch but if this Axios call is inside a Promise already then how do we send that caught error to the catch of the upper Promise?

adarian
  • 334
  • 7
  • 24
  • The promises are nested only because a promise-returning function is wrapped in a new promise. That is improper. – danh Aug 12 '21 at 20:12
  • @danh why is it improper? – adarian Aug 12 '21 at 20:13
  • The new Promise is superfluous. Since `getDataWithoutParams` returns a promise (and so does Axios get). We already have a promise that resolves when the get is complete. Wrapping that in another promise just creates an extra promise – danh Aug 12 '21 at 20:15
  • but what you are saying is just that one should program it differently not providing a solution for this process. Because doing this method may be improper but it works and provides the response correctly the error is just being thrown in the wrong spot. – adarian Aug 12 '21 at 20:20
  • 1
    Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! There should not be nested promise inside `new Promise` at all. – Bergi Aug 12 '21 at 21:03
  • "*Currently, it does not treat the lower error as an actual error in the outer promise.*" - I can not reproduce that with your code. Are you sure your actual code doesn't inadvertently have `.catch(resolve)` (instead of `reject`)? – Bergi Aug 12 '21 at 21:07

1 Answers1

1

Don't wrap Promises

The whole point (or one of them, anyway) of promises is that they don't need to be nested (and shouldn't be).

httpCommonServiceAPI.getDataWithoutParams = (url) => 
  new Promise((resolve, reject) =>                   //*** unnecessary wrapping 
    Axios.get(url)
         .then((response) => resolve(response.data)) //*** unnecessary wrapping
         .catch((error) => reject(error));           //*** unnecessary wrapping

A Promise has 2 "states" and a few substates:

  • Unsettled (we don't know the outcome yet/operation not complete)
  • Settled (we know the outcome)
    • Rejected (failed)
    • Resolved (success)
      • a value was returned
  • [Nested] The Promise was resolved with a Promise. In this case, the state of the current Promise is set to the returned Promise <-- this is what you want

So just return the Promise:

httpCommonServiceAPI.getDataWithoutParams = (url) => 
    Axios.get(url)

or, to just return .data,

httpCommonServiceAPI.getDataWithoutParams = (url) => 
    Axios.get(url)
         .then(response => response.data)

or, as an async:

httpCommonServiceAPI.getDataWithoutParams = async (url) => {
    const response = await Axios.get(url);
    return response.data;
}

All 3 of these examples return Promises.

Putting it together

Using .then .catch

httpCommonServiceAPI.getDataWithoutParams = (url) => 
  Axios.get(url)
       .then(response => response.data)

httpCommonServiceAPI
  .getDataWithoutParams(url)
  .then(responseData => doSomethingWithResponseData(responseData))
  .catch(axiosError => handleFailure(axiosError)) //Axios errors will be returned here

We should not be surprised that this works, since httpCommonServiceAPI.getDataWithoutParams is returning a Promise. If we take the returned object and apply the other calls to it, it would look like this:

Axios.get(url)
  .then(response => response.data)
  .then(responseData => doSomethingWithResponseData(responseData))
  .catch(axiosError => handleFailure(axiosError))  

It should be clear that Axios errors will "bubble up" here.

Using async:

httpCommonServiceAPI.getDataWithoutParams = async (url) => {
    const response = await Axios.get(url);
    return response.data;
}

try {
  const responseData = httpCommonServiceAPI
    .getDataWithoutParams(url);
} catch(axiosError) {
  handleFailure(axiosError); //Axios errors will be returned here
}
Codebling
  • 10,764
  • 2
  • 38
  • 66
  • How will doing this pass the error produced from the Axios Promise to the outermost call? @Codebling – adarian Aug 13 '21 at 05:06
  • @adarian I don't understand -- do you mean the code block at the top, or does it not compute that this is actually returning a Promise, or are you saying that you wish to return an error instead of a Promise? – Codebling Aug 13 '21 at 05:23
  • @adarian I've added another section at the end - I'd previously left this out because it's not clear to me what that code at the top is doing. Let me know if this helps. To me that top block of code is trying to use a function (`getDataWithoutParams()`) before it is defined, and does not do anything with the result, so I'm not sure what it is trying to accomplish. Please clarify the intent/purpose if you'd like more details – Codebling Aug 13 '21 at 05:31
  • check my update to the post for more information. Does that help? – adarian Aug 13 '21 at 05:41
  • Yes, the comment helps. So I think it's case 2 of the 3 listed above. I'll add more explanation – Codebling Aug 13 '21 at 14:43
  • I've added the answer. The error will get bubbled up. You can chain promises like this – Codebling Aug 13 '21 at 14:51