Async / Await Warm Up
If you’re used to using Promises, async / await is an elegant way to make your javascript more legible. It evaluates as synchronous code but returns a promise that executes on the next microtask.
Let’s jump right in and poke around. Open up the babel repl and code along with me. Take a simple function and add async to the beginning:
async function sayHello() {
return 'hello';
}console.log(sayHello()); // => Promise {}
You’ll see that calling this function returns a Promise rather than “hello”. This is because anything returned from an async function is automatically wrapped in a promise. In order to log “hello” we could do this:
sayHello()
.then(str => console.log(str)) // => 'hello'
OK so what about await?
First, write a function that returns a promise:
function mapLater(arr, fn, time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(arr.map(fn));
}, time);
});
}
Pretty straight forward, we map over an array after given amount of time. Now let’s use that in an async function.
async function addAndMultiply(arr) {
const added = await mapLater(arr, i => i + 2, 2000);
const multiplied = await mapLater(added, i => i * 2, 2000);
console.log(multiplied);
}addAndMultiply([1, 3, 6, 20]);
console.log('hello')// => 'hello'
// 4 secs later => [6, 10, 16, 44]
You’ll see that “hello” is logged before we are returned our array. Our async function is non-blocking.
It’s important to note that await must always be inside of an async closure. Calling it in the global scope or in a function without the the async keyword will throw an error.
Handling errors
Imagine something goes wrong with our original mapLater function:
...
setTimeout(() => {
reject('nope');
}, time);
Currently, we have no way of surfacing the error in our async addAndMultiply function. When we run it, the function will fail silently. To handle errors, one solution is to use try / catch:
async function addAndMultiply(arr) {
try {
const added = await mapLater(arr, i => i + 2, 2000);
const multiplied = await mapLater(added, i => i * 2, 2000);
console.log(multiplied);
} catch(err) {
console.error(err); // 2 secs later => 'nope'
}
}
Since errors bubble up by default, another practical solution would be to handle errors at the async entry point:
intricatelyNestedAsyncFunc().catch(err => console.error(err));
Real World
Use it today! So many great libraries provide APIs that return promises. For example, if you’re using fetch to retrieve data, you can start doing things like:
async function getProfileData(id) {
try {
const users = await getUser(id);
...
} catch {
...
}
}
To use async / await now you’ll need to use Babel’s transform-async-to-generator.