Unexpected promise in the bagging area
27.08.20192 Min Read

This week I came across an odd behaviour that it took me a little while to figure out. Take the following example:

const promiseThatWillResolve = Promise.resolve()
const promiseThatWillReject = new Promise((resolve, reject) => reject(new Error(‘Bang’)));

//do some procesing
promiseThatWillReject.then(data => data.map((d, i) => ({index: i, ...d}))

Promise.all([promiseThatWillResolve, promiseThatWillReject]).catch(err => console.error(err))

A less trivial version of this code was in our codebase and when I did some validation on a returning promise, the rejection was unhandled.

My first thought was “surely the catch on the Promise.all should handle any rejections or thrown errors".

I’ve been working with javascript for about 10 years and consider myself very comfortable with most parts of the language yet this was baffling me.

For what possible reason could tacking on a then to a promise cause the error to be unhandled?

It’s at this time I’d like to express that you’re never too good to go back to read the docs. Classic RTFM moment.

As I read the MDN docs and relearned something I’d managed to forget or maybe never knew in the first place, I realised that .then /returns/ a promise.

What does that mean? Well, we’ve created a promise promiseThatWillReject which we’re rejecting at some point in the future. The .then will return a new promise. At this stage we’ve not caught the error. When it rejects, /that/ promise isn’t handling the promise rejection and so it becomes unhandled. In order to fix this, we should use the /new/ promise in the Promise.all so this can pass on the rejection to the catch .

const promiseThatWillResolve = Promise.resolve()
const promiseThatWillReject = new Promise((resolve, reject) => reject(new Error(‘Bang’)));

//do some procesing
const newPromiseThatWillReject = promiseThatWillReject.then(data => data.map((d, i) => ({index: i, ...d}))

Promise.all([promiseThatWillResolve, newPromiseThatWillReject]).catch(err => console.error(err))

This allows us to catch the rejection in the Promise.all and handle it whatever way we want to.