How to Use Async/Await in JavaScript
About the author
@catalinmpit is a software engineer, AWS community builder and technical writer based out of London. He's currently an engineer at TypingDNA, working on applying keystroke dynamics as a means of biometrics authentication.
Check out more of his work on catalins.tech
The purpose of this article is to teach you how to work with promises by using async/await. Why would you want to use async/await, though? The most significant benefit is that it makes the code more readable and cleaner by removing the promise chains.
Let’s see what async/await is and how to use it.
The usual promise
Let's start off with an example of a promise in JavaScript:
fetch("https://api.app/v1/users/cp"); // dummy URL
.then(response => response.json());
.then(console.log);
This probably looks familiar. Now, let's see the same code but re-written with async/await.
async function getData() {
const response = await fetch("https://api.app/v1/users/cp/avatar"); // dummy URL
const data = response.json();
console.log(data);
}
Which one do you prefer? In my opinion, using async/await gives a clearer understanding of the code.
What async/await is
async/await is built on top of promises, and it allows us to write asynchronous code better. It is a new way of
writing asynchronous code instead of using promises and callbacks.
The power of async/await is that it organizes the code making it look cleaner and more "synchronous."
There are two particular keywords:
asyncawait
We use the keyword async at the beginning of the function. Using the async keyword means the function always returns a promise. Also, if we want to use the keyword await inside a function, that function must always start with the keyword async. The code below returns a promise when we call the greeting function. The promise object contains a state (fulfilled or rejected) and a result (in this case, a string).
async function greeting(name) {
return `Hello, ${name}!`;
}
Secondly, we can use the keyword await to wait until a promise fulfills and then return the result.
async function greeting(name) {
return greet = await Promise.resolve(`Hello, ${name}!`);
}
greeting("Catalin").then(console.log); // Returns "Hello, Catalin!"
The above example might not be useful, but imagine when you make an API call (see the example above, in the first section of the article).
Other benefits
Better error handling
Using async/await allows us to handle the synchronous code and asynchronous code in the same construct. Now if there is an error in resolving the promise, the control jumps to the catch block to handle the error.
You can even wrap multiple promises in the same try block, and the code will catch the errors for all the promises,
not just one. It also tells you where the error occurred, in which promise.
async function getUserInfo() {
try {
const response = await fetch("https://api.app/v1/users/cp/avatar"); // dummy URL
const data = response.json();
console.log(data);
} catch (err) {
console.log(err);
}
}
In the above code snippet, the code jumps to the catch block if there is an error.
Better code
async/await allows us to write clear and more understandable code by making it look more like synchronous code. You
can see how much cleaner the code is by looking at "The usual promise" section.
Conditionals
Another benefit of using async/await is that it makes it easier to work with conditionals.
The code snippets below are basic, and they do not handle errors. Their sole purpose is to show the difference
between promises and async/await when using conditionals.
const getUserInformation = () => {
return getUser()
.then(user => {
if (user.profile) {
return getUserProfile(user.id)
.then(userProfile) {
console.log(userProfile);
return userProfile;
}
} else {
console.log(user);
return user;
}
});
};
The above snippet illustrates using conditionals in a promise. Even though the code is not complex, it is difficult to read. Now imagine how complex the code would be if we had more if statements and requests.
Below, you can see the code re-written using async/await.
const getUserInformation = async () => {
const user = await getUser();
if (user.profile) {
const userProfile = await getUserProfile(user.id);
console.log(userProfile);
return userProfile;
} else {
console.log(user);
return user;
}
};
Doesn’t it look better? You can see how converting promises to async/await improves your code’s readability by a significant margin making it easier to understand.
Conclusion
- Adding
asyncto a method header means you'll always return a promise. - Using
asyncalso allows you to use the await keyword, which allows you to wait until a promise is resolved. - Using the
awaitkeyword blocks the code from executing until the promise is resolved or rejected. If the promise cannot settle, it generates an exception you can deal with. async/awaitmakes the code more explicit, easier to understand, and more concise.
Catalin regularly posts helpful development tips and guides on Twitter. Be sure to follow him at @catalinmpit