Retrying Failed Requests

The power of await is that it lets you write asynchronous code using synchronous
language constructs. For example, here's how you might retry a failed HTTP request using the superagent HTTP library using callbacks.

Not too difficult, but it involves recursion and can be tricky to grok for beginners. Plus, there's another more subtle issue. What happens if superagent.get().end() throws a synchronous exception? We'd need to wrap the _request() call in a try/catch in order to handle all exceptions. Having to do this everywhere is
cumbersome and error prone. With async/await, you can write an equivalent function with just for and try/catch:

Trust me, this works. I remember the first time I tried this pattern with co, I was baffled that it actually worked. However, the below does not work. Remember that await must always be in an async function, and the closure passed to forEach() below is not async.

Without async/await, calling next() manually involves the same kind of recursion as the retry example. With async/await, you'll find yourself not using the helper functions anymore (other than maybe toArray()) because iterating through the cursor with a for loop is much easier:

If that's not convenient enough for you, there's a TC39 proposal for async iterators that would let you do something like this. Note that the below code does not work in any currently released version of Node.js, it's just an example of what may be possible in the future.

Multiple Requests in Parallel

Both of the above patterns execute requests in sequence, there's only one next() function call executing at any given time. What about multiple asynchronous tasks in parallel? Let's pretend you're a malicious hacker and want to hash multiple plaintext passwords in parallel with bcrypt.

The Promise.all() function takes an array of promises, and returns a promise that waits for every promise in the array to resolve and then resolves to an array that contains the value each promise in the original array resolved to. Each bcrypt.hash() call returns a promise, so promises in the above array contains an array of promises, and the value of await Promise.all(promises) is the result of each of the bcrypt.hash() calls.

Promise.all() is not the only way you can handle multiple async functions in parallel, there's also the Promise.race() function that executes multiple promises in parallel, waits for the first promise to resolve, and returns the value that promise resolved to. Here's an example of using Promise.race() with async/await:

Moving On

Async/await is a huge win for JavaScript. With these two simple keywords you can remove numerous external dependencies and hundreds of lines of code from your codebase. You can add robust error handling, retries, and parallelization with just a handful of simple built-in language constructs. I hope you're as excited as I am for this feature to hit Node.js 8 LTS (hopefully) in April 2017.

Looking to become fluent in async/await? My new ebook, Mastering Async/Await, is designed to give you an integrated understanding of
async/await fundamentals and how async/await fits in the JavaScript ecosystem in a few hours. Get your copy!

Found a typo or error? Open up a pull request! This post is
available as markdown on Github