DEV Community

Cover image for A simplified guide to Async/Await
Yash Nirmal
Yash Nirmal

Posted on

A simplified guide to Async/Await

Introduction

Asynchronous programming is a crucial concept in JavaScript, allowing developers to execute tasks without blocking the main execution thread. There are a few approaches to handling asynchronous tasks in javascript, but the best one and also the newest one is async/await. async/await can sometimes also be tricky especially if you are new to JS. In this blog, we will explore the async/await syntax in the simplest way possible.

Understanding Asynchronous JavaScript

JavaScript is a single-threaded language, meaning it can only execute one task at a time. However, there are certain tasks, such as making HTTP requests, reading/writing files, or waiting for user input, that can be time-consuming and may cause the program to halt if executed synchronously. Asynchronous programming addresses this issue by executing these time-taking tasks concurrently with the rest of the program, allowing other parts of the program to continue running.

Introducing async/await:

To simplify asynchronous code and make it more readable, ECMAScript 2017 (ES8) introduced the async/await syntax. It provides an easy way to write asynchronous code that closely resembles synchronous code.

Async Functions

An async function is a special function that contains asynchronous code. It allows the use of the await keyword inside it. The await keyword can only be used within an async function, making it an essential part of asynchronous JavaScript programming.

To declare an async function, you can use the async keyword before the function declaration. For example:

async function fetchData() {
  // Asynchronous code goes here
}
Enter fullscreen mode Exit fullscreen mode

Await Keyword

The await keyword can be used to pause the execution of an async function until the asynchronous task is completed. It helps in writing asynchronous code that looks and behaves more like synchronous code, enhancing code readability. Consider the following example that fetches data from an API:

async function fetchData() {
    console.log("Printed before await")
      // await keyword stops the execution of the next lines written after it until the async task(fetch request is not fulfilled)
    const response = await fetch('https://fakestoreapi.com/products');
    const data = await response.json();

    console.log("Printed only after the async fetch request is completed")
  }

fetchData()
Enter fullscreen mode Exit fullscreen mode

In the above code snippet, the await keyword is used to wait for the fetch() function to complete and return a response. Then, the await keyword is used again to wait for the JSON data to be extracted from the response.

Error Handling

Async functions can also handle errors using try-catch blocks. If an error occurs during the execution of the await expression, the control flow jumps to the catch block, allowing error handling. Here's an example:

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error:', error);
  }
}
Enter fullscreen mode Exit fullscreen mode

In the code above, any error occurring during the fetch() or response.json() operations will be caught and logged in the catch block. This makes error handling more straightforward and avoids deeply nested error callbacks.

Sequential Execution

While parallel execution is beneficial for independent tasks, there are scenarios where we need to execute asynchronous operations sequentially, where each operation depends on the result of the previous one. Async/await allows us to achieve this easily. Here's an example:

async function fetchData() {
    try {
      const result1 = await fetch("https://fakestoreapi.com/products");
      const data1 = await result1.json();

      // below fetch call is dependent on above fetch call, since it takes the ouput of the above fetch call
      // below fetch is only executed once the above code execution is complete
      const result2 = await fetch(`https://fakestoreapi.com/products/${data1[0].id}`);
      const data2 = await result2.json();

      console.log(data2);
    } catch (error) {
      console.error('Error:', error);
    }
}

fetchData()
Enter fullscreen mode Exit fullscreen mode

In the above code, the 2nd fetch is taking the input of the first fetch call.

Async/Await with Promises

Async/await is built on top of promises and can be used in conjunction with them. If you have existing code that uses promises, you can still benefit from async/await syntax. Here's an example:

function fetchData() {
  return new Promise((resolve, reject) => {
    // Asynchronous operation
  });
}

async function processData() {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (error) {
    console.error('Error:', error);
  }
}
Enter fullscreen mode Exit fullscreen mode

In the code above, fetchData() returns a promise, and it can be seamlessly integrated with async/await by using the await keyword.

Conclusion

The async/await syntax simplifies asynchronous code by providing a more readable and manageable approach, reducing callback complexity. By using async functions and the await keyword, developers can write asynchronous code that closely resembles synchronous code, making it easier to understand and maintain.

Incorporate async/await into your JavaScript projects to improve code quality, enhance productivity, and unlock the full potential of asynchronous programming.

Also, if you guys want me to write a blog on any other javascript topic, write in the comments. Also, share your views on how this article was.

Thanks for being till the end. Follow me for more.

Top comments (0)