Async/await in Node.js
Older Article
This article was published 9 years ago. Some information may be outdated or no longer applicable.
How to use async/await when working with asynchronous Node.js code.
Async/await is available as part of Node.js since version 7.10.1 (or since version 7.5 with the
--harmonyflag.
From callbacks to promises
In a previous article we covered why promises are a great addition to JavaScript.
Asynchronous programming in Node.js started with the event loop and error-first callbacks. It works, but callbacks bring problems that can cost you sleep.
Native Promise support in Node.js helped by giving asynchronous code a better execution flow. But you still end up with callbacks inside then() blocks when a promise resolves, or inside catch() blocks when one rejects.
Making promises even better
The async and await keywords give developers an even cleaner way to control asynchronous code execution. They let you write code that reads synchronously, with minimal syntax.
If native callbacks were causing callback hell, then async/await is really callback heaven.
To use async/await you still need Promises underneath, so make sure you understand how those work.
The
awaitkeyword can only be used inside functions declared with theasynckeyword.
As the name suggests, await pauses execution until a promise settles. If it resolves, the result can be stored in a variable. If it rejects, you catch it with a catch statement. That means an async function should ideally wrap its logic in a try/catch block.
async/await
Here’s a good use case: multiple promises chained together. Imagine making two API calls and constructing a message from both responses. With plain promises:
const request = require('request');
function makeRequest(url) {
return new Promise((resolve, reject) => {
request(url, (error, response, body) => {
if (error) {
reject(error);
}
resolve(JSON.parse(body));
});
});
}
let message;
makeRequest('https://swapi.co/api/people/1')
.then((character) => {
message = `${character.name} played in`;
return makeRequest(character.films[0]);
})
.then((film) => {
message += ` ${film.title}.`;
return message;
})
.then((message) => console.log(message))
.catch((error) => console.error(error));
Constructing the final message looks tangled. With async/await, the same logic reads much more cleanly. The only thing we keep from the previous example is the makeRequest() method:
const request = require('request');
function makeRequest(url) {
return new Promise((resolve, reject) => {
request(url, (error, response, body) => {
if (error) {
reject(error);
}
resolve(JSON.parse(body));
});
});
}
async function message() {
try {
const character = await makeRequest('https://swapi.co/api/people/1');
const film = await makeRequest(character.films[0]);
console.log(`${character.name} played in ${film.title}`);
} catch (error) {
console.log(error);
}
}
message();
Both await calls pause until their promise resolves. The first character variable (already holding resolved data) feeds straight into the second request.
The code is more readable, easier to follow, and trivial to add breakpoints to.