First things first:

This is specifically about one scenario: avoiding the ‘unhandled promise rejection’ warning when using throw inside an async function in Node.js

inside an async function in Node.js I assume you have a basic understanding of promises

I am by far not an authority on JavaScript. If you find this helpful, great — if you find this confusing or misleading, let me know!

The Problem

You put your code inside an async function in order to use await calls

function in order to use calls One of your await ed functions fails (i.e. rejects a promise)

ed functions fails (i.e. rejects a promise) You throw an error to terminate the script

to terminate the script You get an error message like this: UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().

One reason to deal with (besides the ugly warning) is that the exit code of the process is actually 0 in this case, indicating that it ran successfuly.

(This should change in the future, as the message in the future, promise rejections that are not handled will terminate the Node.js process with a non-zero code suggests.)

TL;DR

Don’t throw inside an async function without catching! You may think that the warning is about the promise from the function you awaited and which failed, but actually it’s about the promise returned from the wrapper function within which you are throwing an error. Async functions return promises implicitly.

Your options:

add .catch() to your wrapper function call (you don’t even need the try/catch block inside the wrapper)

to your wrapper function call (you don’t even need the try/catch block inside the wrapper) use process.exit(1) instead of throw to terminate the script (lazy but efficient…)

Want to know more?

I’ll walk you through my discovery process.

Let’s create an immediately invoked function expression (IIFE) and make it async:

(async function () { })()

(Note: You don’t need an IIFE — what I describe here would also apply to a ‘normal’ function, you would just need to add your .catch() to the function call which can be somewhere else in the code than the function definition. The last () in the IIFE works like a function call.)

Now let’s create a simple function which returns a promise — and always fails (rejects the Promise). We will later try to await this function within a wrapper async function.

function returnsPromise() {

return new Promise(function (resolve, reject) {

reject(Error('I was never going to resolve.'))

})

}

For some time, what I did was I would just wrap my await call in a try/catch block like this and expect it to do the right thing. For example:

(async function () { try {

await returnsPromise()

} catch (error) {

console.log('That did not go well.')

throw error

} })()

But, just as if I didn’t bother with the try/catch block at all, I was getting that same unhandled promise rejection error.

I didn’t understand: wasn’t I handling it? Wasn’t that the point of try/catch ?

I tried replacing the try/catch block with chaining .catch like this…

await returnsPromise().catch(e => { throw e })

…but to no avail. Still the same unhandled promise rejection error.

Then I realised the problem is the throw ! (And the fact that async functions return a promise.)

The code above would actually be fine if it wasn’t for the throw in the .catch() . In fact, this is a perfectly fine way to handle a rejected promise — you could put any logic dealing with it inside .catch() if you wanted to continue your script. The same goes for a try/catch block.

Lesson: don’t just throw an error inside an async function… you have to catch it again.

So what if you want to terminate the script at this point? (Spoiler: you could do it with process.exit(1) , but more on that later. Let’s deal with this properly first.)

How about appending this to the end of the IIFE, then?

(async function () { await returnsPromise().catch(e => {

console.log('That did not go well.')

throw e

})



})().catch( e => { console.error(e) } )

Perfect! We get a ‘nice’ error message with a stack trace and no unhandled promise rejection errors!