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:
async
await
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
async
to a method header means you’ll always return a promise. - Using
async
also allows you to use the await keyword, which allows you to wait until a promise is resolved. - Using the
await
keyword 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
/await
makes 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