loading...
Cover image for An introduction to Async/Await

An introduction to Async/Await

myogeshchavan97 profile image Yogesh Chavan Updated on ・4 min read

Introduction

This is the continuation of the multi-part series. If you missed the previous part regarding the introduction to promises, then you can check that out here.

In this part, we'll explore all about async/await.

What is async/await?

Async/await provides a way to use promises in a better way.

To use the async/await, you need to create a function and add the async keyword before the function name using ES5 function declaration syntax like this:

async function someFunction() {
  // function body
}
Enter fullscreen mode Exit fullscreen mode

or using function expression syntax like this:

const someFunction = async function() {
  // function body
}
Enter fullscreen mode Exit fullscreen mode

or using arrow function like this:

const someFunction = async () => {
  // function body
}
Enter fullscreen mode Exit fullscreen mode

The thing you should always remember is that, when you add the async keyword to the function, it always returns a promise.

Take a look at the below code:

const sayHello = async function() {
 return "Hello";
}

sayHello();
Enter fullscreen mode Exit fullscreen mode

What do you think the output of the above code will be?

Async Function

The output is a promise fulfilled with the string Hello.

So the below code

const sayHello = async function() {
 return "Hello";
}
Enter fullscreen mode Exit fullscreen mode

is same as

const sayHello = function() {
 return new Promise(function(resolve, reject) { 
  resolve("Hello");
 });
}
Enter fullscreen mode Exit fullscreen mode

which is same as

const sayHello = function() {
 return Promise.resolve("Hello");
}
Enter fullscreen mode Exit fullscreen mode

Promise.resolve("Hello") is just a shorter way of creating a promise which resolves to string Hello.

So to get the actual string Hello, we need to add .then handler like this:

sayHello()
 .then(function(result) {
   console.log(result); // Hello
 });
Enter fullscreen mode Exit fullscreen mode

Resolved using async

Now, where do we use the await keyword?

It's used inside the function which is declared as async. So await keyword should only be used inside async function.

You will get an error if you try to use it in non-async functions.

Suppose, we've a promise which returns the product of two numbers like this:

function getProduct(a, b) {
 return new Promise(function(resolve, reject) {
  setTimeout(function() { 
   resolve(a * b);
  }, 1000);
 });
}
Enter fullscreen mode Exit fullscreen mode

and we're using it like this:

getProduct(2,4)
 .then(function(result) {
  getProduct(result, 2)
   .then(function(finalResult) {
    console.log('final_result', finalResult);
   })
   .catch(function(error) {
    console.log(error);
   });
 })
 .catch(function(error) {
  console.log(error);
 });
Enter fullscreen mode Exit fullscreen mode

In the above code, we're first getting the product of 2 and 4 then we're using that result to multiply it by 2 again and then finally printing the product.

If you execute the above code, you will see the final result as 16 which is 2 * 4 = 8 and 8 * 2 = 16.

Async product

The above code of .then and .catch looks a lot complicated and difficult to understand at one glance.

So using async/await we can simplify the above code to this:

const printResult = async () => {
 try {
  const product = await getProduct(2,4); // line 1
  const finalResult = await getProduct(product,2); // line 2
  console.log('final_result', finalResult); // line 3
 } catch(error) { 
  console.log(error);
 }
};

printResult();
Enter fullscreen mode Exit fullscreen mode

This looks much cleaner and easy to understand.

Here, to use the await keyword, we're declaring a function with the async keyword.

Then to get the result of each promise, we're adding the await keyword in front of it.

Also, note that we've added try/catch inside the function. You always need to add a try/catch block around the code which uses await so the catch block will be executed if the promise gets rejected.

There is a very important thing you need to remember: The above async/await code will work exactly the same as we're using .then so the next await line will not be executed until the previous await call is over.

That means, line 2 will not be executed until line 1 is done its execution. Therefore, as the getProduct function is taking 1 second to execute because of the setTimeout call, line 2 will have to wait for 1 second before executing the getProduct function again.

But there is one exception to this behavior, which you can check in my previous article here

Also, if there is an error while executing line 1 (because of some error occurred in the getProduct function), the next code after line 1 will not be executed but instead, catch block will be executed.

Now, If you compare the code of promise chaining and async/await, you will see the difference.

Comparing chaining and async/await

As you can see, the code using async/await is much cleaner and easy to understand as compared to the promise chaining.

As the nesting gets deeper, the code using promise chaining gets more complicated so async/await just provides a way to write the same code but with better clarity.

Using async/await also reduces the need of adding multiple .catch handlers to handle the errors.

We can certainly avoid the nesting in above promise chaining by writing the previous code like this:

getProduct(2, 4)
  .then(function(result) {
    return getProduct(result, 2);
  })
  .then(function(finalResult) {
    console.log('final_result', finalResult);
  })
  .catch(function(error) {
    console.log(error);
  });
Enter fullscreen mode Exit fullscreen mode

Here, from the first .then handler, we're returning the result of getProduct(result, 2).

Whatever returned from the previous .then handler will be passed to the next .then handler.

As the getProduct function returns a promise so we can attach .then again to it and avoid the need for a nested .catch handler.

Avoiding nested handlers

But still async/await syntax looks cleaner and easier to understand than the promise chaining syntax.

Conclusion

In this article, you learned what is async/await and how you can use it to write the promise chaining code with better code readability.

Check out the next part in this series here where you will learn the methods provided by the Promise API

Don't forget to subscribe to get my weekly newsletter with amazing tips, tricks and articles directly in your inbox here.

Discussion

pic
Editor guide
Collapse
nicorafales profile image
Nicolás Rafales

meh

const getProduct = a => b => 
    new Promise((resolve, reject) => setTimeout(() => resolve(a * b), 1000))
;

const printFinalResult = result => console.log('final result', result);

// execute

getProduct(2)(4)
    .then(getProduct(2))
    .then(printFinalResult)
    .catch(console.error)
;

Enter fullscreen mode Exit fullscreen mode

Altough I did find it helpful.
I think ppl that don't like the FP flow might find the thenables more complicated. For me it's the other way around.

notes: The technique of returning a function from a function is called higher order function, which allows you to do a partial-application of a function, something very common on FP languages/frameworks/libraries. The extent of this (when used up until the last parameter) is called currying.

Collapse
myogeshchavan97 profile image
Yogesh Chavan Author

While using this way also works, we should not un-necessarily return a function from another function as the inner function will remain in the memory occupying space even after the outer function has finished executing.

Collapse
nicorafales profile image
Nicolás Rafales

could you provide some feedback on this that demonstrates this is a memory leak?

What happens when we pass a "callback" to an Array.prototype.filter|map|reduce ?

What happens when we use libraries like lodash|lodash FP|Rambda and most of the frameworks we use that jump into callbacks from callbacks?

how do they handle those memory leaks? or do they not?

Thread Thread
myogeshchavan97 profile image
Yogesh Chavan Author

You can check out this article to better understand how memory leak can happen when using closures. In filter, map or reduce we provide a callback function as an argument. We don't return a function from a function there.

Collapse
gregorgonzalez profile image
Gregor Gonzalez

Quick and easy to follow. The first time I had to research in several sources, read a lot and watch several videos and then do tests to answer questions.

Here you clarify everything in a single exercise. You save me a lot of time.

Collapse
myogeshchavan97 profile image
Yogesh Chavan Author

Thank you. I'm glad you found it helpful 🙂

Collapse
isarisariver profile image
Marian Beck

Great article, very well explained. Might have saved me a lot of try and error in the past :)

Collapse
myogeshchavan97 profile image
Yogesh Chavan Author

Thank you 🙂 I'm glad you found it helpful.

Collapse
subashnalam profile image
Subash Nalam

Thank you, it is really helpful to newbies to understand.

Collapse
myogeshchavan97 profile image
Yogesh Chavan Author

Thank you 🙂

Collapse
johnny_bui profile image
Johnny

a simple and elegant explanation !

Collapse
myogeshchavan97 profile image
Yogesh Chavan Author

Thank you 🙂

Collapse
pablofr10 profile image
Pablo Martinez

Amazing and clean explanation. 🤗🤗

Collapse
myogeshchavan97 profile image
Yogesh Chavan Author

Thank you 🙂

Collapse
anishhajare profile image
Anish Hajare

Exactly what I was looking for, every time I had a doubt about async/await promises I would have to go watch some video, very well explained. Heart, Unicorn, Bookmark!

Collapse
myogeshchavan97 profile image
Yogesh Chavan Author

Thank you so much ☺️

Collapse
rajmohanpdy profile image
rajmohan selvaraj

well explained. bookmarked.

Collapse
myogeshchavan97 profile image
Yogesh Chavan Author

Thank you 🙂

Some comments have been hidden by the post's author - find out more