JavaScript async and await - in plain English, please
Async and Await in JavaScript is the most reasonable way to handle promises and asynchronous operations. But, there is more to it. Let's explore.
JavaScript developers love using async-await
. It is the most straightforward way to deal with asynchronous operations in JavaScript. Suppose we do a poll of usability between the async/await
syntax vs. the promise.then()...then().catch()
, async/await is going to win with a significant margin. However, we may ignore something important here.
It's not just about the syntax and usability that we need to compare them with. We shouldn't even compare async/await and the plain-old way of handling promises. There are various use-cases and chances that we may use them together. Also, the understanding of promises is the essential part of appreciating the existence of async/await.
Welcome to the third article of the series, Demystifying JavaScript Promises - A New Way to Learn. Let's start discussing
async/await
. If you are new to the series, please feel free to check out the previous posts,
If you like to learn the async/await keywords from video content as well, this content is also available as a video tutorial here: ๐
Please feel free to subscribe for the future content
The async/await
are Keywords
JavaScript offers us two keywords, async
and await
, to make the usage of promises dramatically easy. The async and await keywords contribute to enhancing the JavaScript language syntax than introducing a new programming concept.
In plain English,
- We use
async
to return a promise. - We use
await
to wait and handle a promise.
Let's expand it further to understand the concepts better.
The
async
keyword is for a function that is supposed to perform an asynchronous operation. It means the function may be taking a while before it finishes execution, returns a result, or throw an error.We use the
async
keyword with a function as,async function fetchUserDetails(userId) { // pretend we make an asynchronous call // and return the user details return {'name': 'Robin', 'likes': ['toys', 'pizzas']}; }
With the arrow function,
const fetchUserDetails = async (userId) => { // pretend we make an asynchronous call // and return the user details return {'name': 'Robin', 'likes': ['toys', 'pizzas']}; }
So, what does the async function
fetchUserDetails
returns when we invoke it? It returns aPromise
.The difference between a regular function and an
async function
is, the latter always returns a promise. If you do not return a promise explicitly from an async function, JavaScript automatically wraps the value in a Promise and returns it.The
await
keyword is for making the JavaScript function execution wait until a promise is settled(either resolved or rejected) and value/error is returned/thrown. As thefetchUserDetails
async function returns a promise, let us handle it using theawait
keyword.const user = await fetchUserDetails(); console.log(user)
Now, you will see the returned user object in the console log,
You would have used the plain-old
.then()
method to handle that promise without theawait
keyword.fetchUserDetails().then((user) => console.log(user));
A Few Rules about using async/await
We need to understand a few simple rules to use the async and await keywords.
You can not use the
await
keyword in a regular, non-async function. JavaScript engine will throw a syntax error if you try doing so.function caller() { // Using await in a non-async function. const user = await fetchUserDetails(); } // This will result in an syntax error caller();
The function you use after the
await
keyword may or may not be anasync
function. There is no mandatory rule that it has to be an async function. Let's understand it with the following examples,Create a non-async function that returns the synchronous message, say,
Hi
.function getSynchronousHi() { return 'Hi'; }
You can still use the keyword
await
while invoking the above function.async function caller() { const messageHi = await getSynchronousHi(); console.log( messageHi); } caller(); // Output, 'Hi' in the console.
As you see, we can use the
await
with a non-async function but, we can not use it within(or inside) a non-async function.The V8 engine(version >= 8.9) supports the top-level await in modules. It means you are allowed to use it outside of an async function. The Chrome DevTools, Node.js REPL support the top-level await for a while now. However, it is still not supported beyond the environments we just discussed.
To use the top-level
await
in a non-supported environment, the solution is to wrap it into an anonymous function, like this,(async () => { const user = await fetchUserDetails(); })();
How to Handle Errors with async/await
?
We learned about error handling using the .catch()
handler method in the promise chain article. If the promise rejects, it throws the error, and we need to catch it to handle it.
With the async/await
keywords, we can handle the error with traditional try...catch
. When there is an error, the control goes to the catch block. Please have a look at the example below.
Assume we have a function that validates if the userId
and password
are blank. If so, throw an error by rejecting the promise. Otherwise, resolve it with a success message.
const validateUser = ({userId, password}) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (userId && password) {
resolve(`${userId} you have been authenticated successfully!!!`);
} else {
reject({message: 'userId or Password could be blank!'});
}
}, 2000);
});
}
As the above method returns a promise, we can handle it using the await
keyword. Let us focus on the case where we pass the userId and password as empty strings.
const app = async () => {
const data = {
userId: '',
password: ''
};
try {
console.log('Initializing...');
const result = await validateUser(data);
console.log(result);
} catch (e) {
console.error(e.message);
}
}
// invoke the function app
app();
When we invoke the app()
function, the validateUser(data)
will throw an error implicitly. We handle it using the try...catch
in the app()
function. The control will go to the catch
block. We will get the error log as,
If we pass valid userId
and password
values, we will see the expected result log in the console.
Can We Write the PizzaHub
example with async/await
?
Surely, I think that's a great idea. We have created APIs and the methods to handle the pizza order in the Robin and the PizzaHub Story. Remember the orderPizza()
function? We handled the promises using the .then()
and .catch()
handler methods.
Let's rewrite the orderPizza()
function using async/await
. You bet, it is a much simplified version as we see below,
async function orderPizza(type, name) {
try{
// Get the Nearby Pizza Shop
const shopId = await fetch("/api/pizzahub/shop", {
'lang': 38.8951 ,
'lat': -77.0364});
// Get all pizzas from the shop
const allPizzas = await fetch("/api/pizzahub/pizza", {
'shopId': shopId});
// Check the availability of the selected pizza
const pizza = await getMyPizza(allPizzas, type, name);
// Check the availability of the selected beverage
const beverage = await fetch("/api/pizzahub/beverages", {
'pizzaId': pizza.id});
// Create the order
const result = await create("/api/order", {
beverage: beverage.name,
name: name,
type: type,
});
console.log(result.message);
} catch(error){
console.error(error.message);
};
}
Please find the complete source code from here. So now you know how to write the orderPizza()
function using both the async/await and plain-old promises respectively.
Do you want to guess or try how it would look using the JavaScript callback
functions? Please take a look from here. I hope you appreciate the world of promises and async/await a lot more now ๐.
So, What's Next?
Thank you for your effort to learn and master JavaScript Promises. It is indeed an essential aspect of the language. Up next, we will learn about Promise APIs
. Promise APIs
and the async/await
keywords make it a much better experience in handling promises. We will learn about it using visual demonstrations and examples.
Until then, please enjoy learning and stay motivated. You can find all the source code used in this article from this Github repo,
I hope you enjoyed this article or found it helpful. Let's connect. Please find me on Twitter(@tapasadhikary), sharing thoughts, tips, and code practices. Would you please give a follow? You can hit the Subscribe button at the top of the page to get an email notification on my latest posts.