Error Handling When Chaining Promises

November 23rd 2018 JavaScript

The Promise.catch method is a convenient tool for handling errors anywhere inside the promise chain up to the method call.

Promise.resolve().then(() => {
    // chained code, executed after outer promise is resolved
}).catch(() => {
    // handling errors in chained code and rejected outer promise
});

However, care must still be taken when writing the chained code inside the Promise.then method, otherwise some errors might not be properly handled.

Synchronously thrown errors will always be caught as expected:

Promise.resolve().then(() => {
    throw Error();
}).catch(() => {
    // the error thrown above will be caught as expected
});

The same cannot be said for rejected promises. In the following code snippet, the promise rejection will not be caught by the catch method. Can you notice why?

Promise.resolve().then(() => {
    Promise.reject();
}).catch(() => {
    // the code here will not execute after the inner promise is rejected
});

Of course, one could always call the catch method on the inner promise to handle its rejection:

Promise.resolve().then(() => {
    Promise.reject().catch(() => {
        // this code will handle the rejection of the inner promise
    });
}).catch(() => {
    // this code would handle the rejection of the outer promise
});

Although this would work, such code soon gets difficult to maintain, as each promise requires separate error handling code which will usually be identical.

Fortunately, there's a better way to do it. For the outer catch method to catch inner promise rejections, these inner promises need to be returned from the then method callback:

return Promise.resolve().then(() => {
    return Promise.reject();
}).catch(() => {
    // this code will handle the inner and the outer promise rejection
});

The recipe for successfully handling promise rejection is really quite simple: just make sure that any promise you invoke:

  • is either directly followed by a catch method call
  • or is returned from a callback of a then method of an outer promise.

That outer promise must follow the same rule of direct or indirect handling of errors. In the most extreme scenario, only the outermost promise must have its own catch method call. All other promises inside it can simply be returned and handled by that outermost method.

Copyright
Creative Commons License