In this article we'll be discussing how to use the async/await keywords 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
--harmony
flag.
In a previous article we have discussed why promises are a great addition to the JavaScript language.
In a nutshell asynchronous programming was first achieved by Node.js using the event loop and the error first callbacks. Although this works, there are multiple problems with using callbacks that may give developers some sleepless nights.
Native support in Node.js for Promises have helped the creation of code where asynchronous programming was enabled by a better flow of programme execution. However, callbacks are still used inside then()
statements when a promise is resolved or even inside a catch()
block when an error is rejected.
One of the latest additions to the JavaScript language are the async
and await
keywords that give us, the developer, an even better way of controling the asynchronicity of code execution. Using async/await truly give us synchronous code execution with a really simple syntax.
If native callbacks were causing callback hell, then async/await is really callback heaven.
In order to use async/await we need to also utilise Promises, so it's important that you understand how they work.
Please note that the
await
keyword can only be used within functions declared with theasync
keyword.
As it's name suggest - the await
functions allows us to await for a promise - in other words if the promise resolves, its result can be stored in a variable. If the promise rejects it can be caught using a catch
statement, which means that ideally a function declared as async should utilise a try/catch
block as well.
Let's take a look at an example where we'd be using multiple promises - this is especially a good use-case for async/await. Consider a scenario where we are making multiple requests to an API and would like to construct a message from the responses from both API calls, this is how our could could look like:
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 a bit complicated. Using async/await
will make the code look a lot more readable. The only thing that we'll save from the previous code example is going to be 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();
Notice how we are using await
to wait for both of the promises to be resolved and we can also make use of the first character
variable (containing the resolved promise data already) and use it to make the second request with the second await
.
The code above is a lot more readable, easy to follow and it's straight forward to add break points to it.