The simplest way to handle an error when using
async/await is to wrap the lines using a
await operator in a
try block and handle the error on the subsequent
Looks like this, if you’re wondering:
The use of the
try/catch statement with
async/await makes sense: since the
await operator allows us to write asynchronous code in a way it looks synchronous, we can also handle errors as if they were synchronous.
catch block we are free to put any code we want. It can be a function that rollbacks some database operation or just a
console.log to print the error.
Note: I’ve seen many people put a single
throw err inside of the
catch block. That is redundant. If something goes bad on your function call it will just throw anyway (you don’t need to use
Next sections will cover a bit of how we can structure our error handling code with
async/await without just blindly wrapping things in a
try/catch every time we use
await. Spoiler: you don’t even have to use the
try/catch if you want.
I will be using as example a simple Movie application scenario where we have a main function that calls two other helper functions.
First, we will write it taking a conservative approach and wrapping every occurrence of the
await operator inside of a
In the code above we are handling errors inside of the
saveActors functions. A third
try/catch error handler is also inside of our main function, which calls
I did not specify how errors are handled inside of the
catch blocks but here are two possible outcomes:
- If the errors inside of
saveActorsare thrown, they will bubble up to the outer level
catchin the main function;
- If the errors inside of
saveActorsare “ignored” (eg.
console.log(err)) then the outer level
catchin the main function doesn’t have any use.
On this pattern I am interested on creating a “catch-all” error handler and reduce the unnecessary
try/catch verbose on the code. To do that I can simply drop the first two
try/catch blocks and handle the error only inside of the main function:
Done! We can now handle the errors generically inside of the catch-all handler and use
async/await in the help functions without worrying about errors.
The main idea here is to handle errors on the most outer level as possible.
One good thing about this way of handling errors is that you can still write a separate
try/catch to handle specific errors. This opens doors to the next section:
A catch-all error handler doesn’t mean you can’t handle any specific errors when they happen. Any error handling code before the catch-all handler will intercept the error and you can choose between doing something or pass it to the catch-all handler.
To show it, let’s zoom in on the
saveMovie function. I’ve added a call to a fake IMDB API. There we are interested on two types of errors: token expiration and “not found” HTTP errors:
catch. We need to either rely on
instanceof checks or, depending on the error object, do some string comparison. The manual
throw at the end doesn’t help it look better as well.
It gets the job done but, because of these reasons and all the nesting, this pattern can look like a workaround. Let’s see some alternatives.
async/await is just some nice syntax sugar built on top of promises so we can always fallback to using Promise methods if we want. This means we are free to handle errors the same way we used to do with promises: by chaining the function call with one or more
.catch() methods at the end.
Let’s put up some work on the last example and see how I can rewrite it:
There’s nothing new about this code, right? It’s just plain old promises. The only difference is that, instead of waiting for the value inside of
then(), we paused the execution with
await until the Promise resolves.
Although this one is a npm package and not a language feature, I really think it deserves a place here.
The library is a wrapper function that leverages ES6 destructuring to make error handling similar to the way it is in Go. The code is very small, easy to understand, and you can replicate and adapt it to your codebase without much effort.
Let’s see how it works:
Looks like synchronous code, right?
Even tough the library is very opinionated on how you structure you code, it makes for a very clean solution. It reduces the nesting of using
try/catch blocks and makes the code more readable.
This article explored a few different ways of handling errors besides the simple
try/catch block and aims to extend upon the multiple error handling tutorials for
async/await out there.
I’ll leave it up to you to decide what fits best on your code (and your mind). Also don’t stop here: you can combine some of these patterns together and create a different one. If you do so, let me know how it turned out!