One of my co-workers jokingly responded after I said I would never use ES7’s async/await over Bluebird’s
Promise.coroutine with this:
Year 2040, Man refuses to acknowledge async/await and insists on using coroutines
What is an async function?
Below is an example of an async function:
The code above is extremely clear: wait for 2 seconds and log. Async/await eliminates the need for timeout callbacks by allowing to specify waiting on asynchronous actions.
Why have I not been using async functions?
Async functions become more complicated when you have to handle rejected promises. Take the code below, which fetches a Bookshelf User model by email, adds a token on to it, and returns a response from a server:
Let’s say if
[email protected] is not found in the database when the user is fetched, the fetch promise rejects with an error called
UserNotFoundError. We want to respond with a 404 for that particular error type and allow all other errors to throw and be caught by our server. There are two ways to handle an error with an async function:
- A try/catch block inside the async function.
- A catch block where the async function is called.
There are plenty of examples online about the first option, but in this particular case, I want to focus on #2. Here is what our implementation could look like:
The problem here is that the
catch block passes all error conditions to one function callback. If there is more than one error type, you need multiple conditional
instanceof checks, which makes your callback longer and more complex. This may not seem like an issue, since we do handle the
UserNotFoundError. Nevertheless, we have to re-throw if the error does not match conditional
instanceof check. If you forget to re-throw, this could cause your server to hang in that block because it has not escaped asynchronous execution.
Bluebird (and in particular,
Promise.coroutine) diverges from async functions because it supports a feature called filtered catch. This feature allows you to specify one or more error constructors to be handled by a specific callback. This is available in many other languages, such as C# and Java, and is incredibly useful for handling different exception types.
Promise.coroutine we can make use of this feature in a similar manner to async functions:
Filtered catching is not supported by the ES6’s native Promise implementation, which is why I have avoided using async/await and have stuck to using
Promise.coroutine. The problem is clear: async/await returns a promise that does not support the useful, granular filtered catch that I need to cleanly handle the multiple different types of errors I could experience inside these functions.
My solution to “error handling hell”
To get filtered catch support, I need async functions to return a Bluebird promise. Bluebird has a utility function called
Promise.method, which we can wrap our async function with to return a Bluebird promise with filtered catch support:
Now, our called promise error handling looks exactly like it did with
This is much cleaner, and now gives async/await functions all the power of Bluebird’s
Why is this a big deal?
Particularly with Node, I find myself performing multiple asynchronous actions that return different error types. Filtered catch allows you to clearly express your exception handling with discrete catch blocks, which has an added benefit of making your error handling code more readable. It is the reason why I still use Bluebird over native ES6 promises, and I will most likely continue to do so until it becomes a feature in native promises.
I made an example with a custom error type here on JSFiddle, go check it out!
Another way to solve error handling in async function is to use Dima Grossman’s await-to-js package. Instead of using Bluebird promises, he uses a wrapper to return errors like the Go programming language. The wrapper returns a 2-element array with the first element being an error/null and the second element being the resolved value. The syntax becomes much cleaner with ES6 using destructuring statements, and is an interesting take on the issue. You can read more in his announcement/blog post here.
Thanks for reading!