async & await

JavaScript promises were a revelation in JavaScript, providing an alternative to the JavaScript callback hell we’d quickly found ourselves in. Promises also allowed us to introduce and better handle asynchronous tasks. While promises were an improvement over callback hell, they still require lots of thens which can become messy. I’ve really taken to ES2017’s async and await keywords for simplifying promise handling. Let’s have a look at async and await!

Quick Basics

async is a keyword for the function declaration

await is used during the promise handling

await must be used within an async function, though Chrome now supports “top level” await

async functions return a promise, regardless of what the return value is within the function

async/await and promises are essentially the same under the hood

Available now in most browsers as well as Node.js

Benefits of async and await

Your code is more simplistic, precise

Debugging is easier thanks to less callbacks

Conversion from promise then / catch code is easy

Your code can be more “top down”, less nesting

Basic async and await Illustration

It’s always easiest to learn from an example, so let’s have a look at a very simple async / await usage:

Start by declaring the function as async; this declaration allows await to be used from within. The await keyword is then followed by a promise-yielding action, which of course the fetch API is. The asynchronous routine (fetch in this case) runs and execution of further code halts (though not blocking) until the async action finishes. The function then resolves with the return value and a promise is returned.

Essentially you get to keep your code “inline” without the need for callbacks. It’s async made a bit more simple!

Converting Promise Handling to await

There’s a good chance you’ll want to update your promise code when time becomes available. Let’s walk through updating promise to await:

Object & Class Methods

As you can see, adding async is really easy and accommodates all function creation workflows!

Error Handling

Traditional promise use allows you to use a catch callback to handle rejection. When you use await, your best bet is using try/catch:

try { let x = await myAsyncFunction();
}
catch(e) { // Error!
}

The old try/catch isn’t as glamorous as a promise’s catch callback but is just as effective.

Parallelism

Google’s Jake Archibald make excellent points in the Async functions document about not getting too sequential with your awaits. The idea is to avoid stacking awaits, when possible, and instead trigger tasks immediately and use awaitafter said tasks are triggered:

The first block is bad because the second fetch happens after the the first fetch completes. The second block is a better method: trigger both fetch calls and then use await ; doing so allows the fetches to happen concurrently!

Promise.all Equivalents

One of my favorite functions of the Promise API is Promise.all, which fires a callback when all fetches are complete. There’s no direct async / await equivalent but this post provides a good equivalent:

let [foo, bar] = await Promise.all([getFoo(), getBar()]);

Remember that async / await are essentially the same as promises deep down, so we’re simply awaiting the aggregated promise to be resolved!

You can now use async and await in all major browsers. These new keywords are also available within Node.js; older Node.js versions can use the transform-async-to-generator babel plugin to use async and await today. Promises are still excellent but are made more maintainable with async and await!