GuideWeb Development

Async/Await for Beginners— Understanding Asynchronous Code in Javascript

Have you ever struggled with understanding Async / Await? — A beginner-friendly introduction to this new syntax and its purpose.

Konstantin Münster Avatar
Konstantin Münster
21 September 2019
8 min read
Read on Medium
Async/Await for Beginners— Understanding Asynchronous Code in Javascript

Being introduced in ES7, the Async / Await syntax finally made it into JavaScript as a standard. But what’s so special about it and why do we need it at all? As I am a beginner myself, I struggled a lot with understanding and handling asynchronous code in JavaScript in the past. Therefore, this article should be an introduction to JavaScript’s problems with asynchronous code and how Async / Await can help us in solving them.

What is Asynchronous Programming?

To truly understand why we need things like Async / Await, we have to take a look at JavaScript’s core concepts. At its base, JavaScript is a synchronous programming language. This means it is single-threaded. Only one operation can be executed at a time. I visualized in the following graphic why this can lead to problems.

Asynchronous Execution in Programming
Synchronous and asynchronous execution in Javascript

By default, the code execution in JavaScript is synchronous. This means that each operation blocks the following until it’s done. Therefore, operations which typically take longer (e.g. network requests) will block any further execution. This is quite bad and will naturally lead to performance issues as you can grasp from the graphic.

However, through features like the Event Loop, today’s JavaScript found ways to execute code asynchronously. Thus, it can perform operations seemingly concurrently and not only sequentially.

As shown in the graphic, the asynchronous way of handling operations is by starting them and then waiting for their responses in the future. Thus, time in between can be used for other operations. With this mechanism, long performing operations are no longer blocking your code from further execution.

The Problem of Asynchronous Execution

But… this asynchronous execution can cause problems too. What if we, for instance, make a network request in line 1 and continue working with the requested data in line 2? Since we only started the network request in line 1, we can not be sure that it is already resolved in line 2 — probably, we will get the response way later. Therefore, the execution in line 2.

So, we need a way to declare if a piece of code is dependent on some preceding asynchronous executed code. Speaking in our example, we have to make sure that line 2 is only executed once the response of the network request of line 1 has been received.

Understanding Async / Await

Now as you might guess: Async / Await solves exactly this problem for us. But to be fair, it is not the first approach to help us handling asynchronous code in JavaScript. In fact, there have been two decent ways before: Callbacks and Promises. However, since both have some downsides regarding readability and code maintenance, with Async / Await we now have a third option to handle asynchronous code which is far easier to read and understand, especially for beginners.

So, the main reason why the Async / Await method made its way is that it makes asynchronous and synchronous code more or less indistinguishable. That, in turn, brings a new level of clarity and simplicity to handling asynchronous code which wasn’t there before.

A simple Async / Await Example

But let’s see Async / Await in action now! In the example below, we want to fetch some posts from an API. Since this can be a long performing operation, it is handled asynchronously by default. Hence, we somehow need to make sure that the fetch call in line 3 has been successfully resolved before we continue working with that fetched data in line 4. Otherwise, the json method will fail since we don’t have a response from the fetch call yet.

const fetchData = async url => {
  try {
    const response = await fetch(url);
    const data = await response.json();
    return data;
  } catch (error) {
    // Handle errors as needed
  }
};

fetchData('https://example.com/posts');

With Async / Await we can ensure that easily. First, we declare the fetchData function as an asynchronous function by using the async keyword. By using this declaration, JavaScript allows us to use the await keyword inside our function. And this keyword really does the trick! Whenever it occurs, JavaScript pauses the execution of our function until the asynchronous call (e.g. our fetch call) has been resolved. Once that is the case, it continues the execution. So the procedure of line 3 is straightforward in our example:

  1. We start the fetch call with the given URL
  2. We wait until we receive its response and store it in the variable
  3. We continue with the next line of code

Then, we do the same thing for the json call in line 4 and finally, we return the received data in line 5. It’s easy as that!

Error Handling with Try & Catch

Probably, you wondered why we have the Try and Catch blocks in our example. Those are responsible for any error handling we might want to implement. As you can imagine, not every fetch call will resolve successfully. There might be the case that the URL we want to request does not exist. In such cases where we can not successfully resolve an asynchronous call, the further execution would fail. Therefore, we can specify in a separate catch block, how we want to treat such errors (like displaying an error message to the user). So if any of our await statements in the try block fails, the execution jumps right into the catch block.

Mastering Async / Await

Now, that we got the basic concept of Async / Await, there’s just one piece missing to fully master this new syntax. We need to understand Promises. Promises are, as I mentioned earlier, another way of handling asynchronous code in JavaScript. They build the foundation of Async / Await — which is in the end only syntactical sugar on top of Promises. Therefore, understanding the concept of Promises is key to really master Async / Await.

Basically, a Promise is an object which may produce a value in the future — depending on whether the asynchronous call will resolve successfully or will be rejected. What is meant by this can be seen in the graphic again: We start an operation at the beginning and this operation promises, so to say, that it will return a response later on.

Important to know is that every Async / Await function returns such a Promise. Since it is an asynchronous operation, it returns a response in the future which we then have to resolve.

Async / Await always returns a Promise

In our example, the fetchData function in line 11 returns a Promise instead of the actual data we want to have. To retrieve the data, we have to wait until the Promise has been resolved (meaning the response of our asynchronous fetchData function has been returned). We can do that by using the then method. This function takes another function as an argument where we pass in the response. In the function body, we can then work with the actual received data of our fetchData call.

const fetchData = async url => {
  try {
    const response = await fetch(url);
    const data = await response.json();
    return data;
  } catch (error) {
    // Handle errors as needed
  }
};

fetchData('https://example.com/posts').then(json => console.log(json));

As you can see, Async / Await is a really simple and clean syntax to handle asynchronous code in JavaScript. However, if you want to work smoothly with this new method, there is no way around Promises since they build the foundation of it. Therefore, learning them too is highly recommended.

I hope this article helped you in understanding what Async / Await is all about!