Why Is Javascript Await Async So Famous?

Why Is Javascript Await Async So Famous?


  • Share on Pinterest

In this article, we are going to see through examples, some use cases of asynchronous programming in Javascript, and why the usage of async-await pattern in Javascript is so famous and convenient to use while coding.

The async-await pattern is a common syntactic pattern that many programming languages are using. It allows the execution of asynchronous, non-blocking code as if it was synchronous.

As a concept is related to the coroutine concept and it provides the ability to a piece of code to execute a different snippet of code while waiting for a long-running, asynchronous task to complete.

Synchronous vs Asynchronous

In a synchronous programming model, each task is performed one at a time sequentially. So in order for a step to execute the previous step has to be complete. 

As a result, long-running operations will block the rest of the program until the synchronous operations are complete.

On the other hand in the asynchronous programming model, operations take place in a non-sequential manner. Asynchronous non-blocking operations do not wait for other operations to complete.

Once an asynchronous operation is complete, successfully or unsuccessfully, it raises an event that notifies the rest of the code with the results and that is ready for the next step to take control.

Callback functions

One way to approach asynchronous programming is using callback functions.

A callback is a function that will execute from another function when a specific event occurs. The callback function will be passed as a value to the caller function.

Let’s see some common examples of callback functions

A common use case scenario of callback functions is with DOM events

See the Pen
callbacks with dom events
by codebitshub (@codebitshub)
on CodePen.

In the code above I am waiting for the DOMContentLoaded event to fire.

Next, I am getting the div element from the HTML document and changing its styles when the mouse enters and exits the div element using the mouseover and mouseout events.

Moreover, I am using the addEventListener method to add the callback functions to these events.

The invocation of a callback function could take place for any kind of event not only DOM events even custom events or after a time period ends. 

Let’s see an example with timers.

See the Pen
callback with timers
by codebitshub (@codebitshub)
on CodePen.

In the code above, as you see in the logs the callback function will be executed after the expiration of the defined time period.

Error Handling with callback functions

Traditionally errors are handled in callback functions as arguments that are passed in the callback functions.

Let’s see an example of reading a file that does not exist in the NodeJS environment.

As you can see in the code above I am attempting to read a file that does not exist in the system.

As a result of this, an error object will be sent to the callback function that I can detect and log as you see below.

Received error [Error: ENOENT: no such file or directory, open ‘a_file_that_does_not_exist.txt’] {
errno: -2,
code: ‘ENOENT’,
syscall: ‘open’,
path: ‘a_file_that_does_not_exist.txt’
}

Callback Hell

Callback functions traditionally are great to use for simple cases. The problem is that they can introduce multiple levels of nesting that can real quick make your code hard to understand and maintain.

It is more likely to face it when you are working with tasks that are asynchronous by nature like working with APIs, reading files from disk, retrieving data from the database, or downloading files from the internet.

Let’s see an example working with fake user data that are provided from the gorest.co.in API.

In the example above I am using the GET endpoint https://gorest.co.in/public/v2/users/{user_id} in order to retrieve a user’s data.

My goal is to get the data for three users and log them into the console.

For that purpose, I am using the XMLHttpRequest npm package to retrieve the data in the NodeJS environment.

As soon as I get the data for user2349 I am triggering a second XMLHttpRequest to get the data for user2350. The same logic is applied for user2352.

Finally, when all the requests are complete I am logging their data to the console.

Now you can see that even if I am getting the data only for three users the code is very clean and cannot be read easily.

Even if I split the callback functions in separate declarations the improvement is not significant as you can see in the file not_nested.js in the previous example.

Often this problem is mentioned as callback hell and can be a serious issue when you are developing large applications.

Nowadays the use of Promise and of the syntax async/await provide a much cleaner way to write asynchronous code in Javascript. 

Let’s see some examples in the next sections.

Promises

Promises are special objects that are used to handle asynchronous tasks. It is defined as a proxy for a value that is not currently available but it will become as soon as a specific task is complete. 

You can use them in order to avoid the callback hell that I described in the previous section.

A Javascript promise object has two properties named state and result. The state property can have the values ‘pending’, ‘fulfilled’ and ‘rejected’. Depending on the state value the result property is modified accordingly as you can see in the table below.

state result
pending undefined
fulfilled a result value
rejected an error object

Since this article is about async/await and why it is famous, I will just give some basic examples of creating and using Promises in order to keep it as short as possible.

How to create a Promise

You can initialize a promise object using the Promise constructor that the Promise API exposes and the syntax new Promise()

The Promise constructor accepts two functions as parameters named resolve and reject

At first, when you create the Promise object will be in a pending state. Then depending on which function will be called the promise will be either fulfilled or rejected with the value/error that is passed in the functions resolve/reject respectively.

How to consume a Promise

A promise can be consumed using the methods then and catch following the syntax below.

.then((value) => {…}).catch((error)=>{…})

Each of these methods accepts a callback function that will return the value in which the promise is fulfilled or rejected.

Let’s see an example

As you can see in the code above after 3 seconds the message “I am done” will appear in the console which is the resolved value.

Another important property of promises is chaining. So if the first then() method returns another promise object it can be chained with a second then() method. 

Error handling with promises

Errors in promises can be handled with the usage of the catch method as we saw above. When a promise fails or is rejected the catch method will take control and its callback will be executed.

Let’s see an example

As you can see in the code above after 3 seconds the message “An error occurred” will appear in the console which is the rejected value.

How to handle callback hell with promises

Now that you have a picture of promises, let’s see an example of how you can handle the callback hell issue using promises.

In the code above I am using the node-fetch package to call a RESTful endpoint in order to retrieve data from an API. 

In the first then() method I am returning the value of the text method which again is a promise. Finally in the third then() method I can log the data for the user2349.

I repeat the same process for each user and I am saving the user data in an array named users.

In the final step, I can log the data that I stored for all the users.

As you can see it is more readable and less complex. If you need more information about promises you can read here.

Async Await

Async/await syntax was introduced in ECMAScript 2017 of Javascript and is built on top of promises. It is syntactic sugar that helps to write and maintain easier asynchronous code.

It will make asynchronous code looks synchronous and maintain the asynchronous nature of the code at the same time.

Also, it will help you to avoid the callback hell issue that I described in the previous section.

So let’s see some examples of how you can use it.

In order to use it, you should write the keyword async in front of a function declaration. This will convert the function to asynchronous and it will return a promise. 

Next in order to get the resolved or rejected value you should use the keyword await in front of the async function invocation. 

This will force the javascript engine to wait for the promise to resolve or to reject.

Following the async-await syntax will help you to avoid chaining of then() methods in promises and write more readable code that will look synchronous.

Let’s see our previous example with the callback hell issue and how it looks like using the async/await approach.

As you can see, the code is much cleaner and easy to read compared to the Promises approach. Also, it is shorter and easier to maintain.

This is the benefit that you earn using the async/await syntax and the main reason why this approach is popular and famous among javascript developers.

Summary

Let’s summarize what we have seen in this article

  • What synchronous and asynchronous programming is
  • How to approach asynchronous programming with callback functions
  • What a callback hell is
  • How to avoid callback hell using promises and async/await syntax

Thank you for reading this long article. I hope it will help you to understand better the topic of async/await syntax. 

As I mentioned before, the reason that the syntax async-await is so famous in Javascript coding is that it will help you to avoid the callback hell issue, to write less code in a more readable and cleaner way. 

Happy coding!

LET’S KEEP IN TOUCH!

We’d love to keep you updated with our latest news and offers 😎

We don’t spam! Read our privacy policy for more info.