DEV Community

Rodrigo Castilho
Rodrigo Castilho

Posted on • Updated on

Async/Await — Understanding JavaScript API Requests and Responses in the Data Fetching lifecycle

Async/Await

Before this article, I mentioned to you that I would start the articles series and I already created an Introduction, V8 JavaScript engine, Callbacks, AJAX and XMLHttpRequest, Synchronous and Asynchronous, and about the Promises. If you lost what I did, check it:

In this article, I’ll not explain the concept of Fetch API but don’t worry that I’ll explain it in the next article.

The async and await is a way to write asynchronous JavaScript code that is more readable and easier to reason about, and it provides a more straightforward and less error-prone way of dealing with promises than traditional promise chains.

The async keyword is used to define a function that returns a promise. This allows you to use the await keyword inside the function to wait for asynchronous operations to complete.

The await keyword can only be used inside a function marked as async (async function). When you use await outside of an async function, you’ll get a syntax error.

There are some ways to declare (define) JavaScript functions using async:

    // function declaration
    async function foo() {
      return "Lorem";
    }

    // function expression
    const = async function foo() {
      return "Lorem";
    }

    // arrow function
    const foo = async () => {
      return "Lorem";
    };

    // arrow function (shorthand)
    const foo = async () => "Lorem";

    // Immediately Invoked Function Expression (IIFE) 
    (async () => {
      return "Lorem";
    })();
Enter fullscreen mode Exit fullscreen mode

Following async/await can make your code more readable in many cases. By using async/await, you can write asynchronous code that looks more like synchronous code, which can make it easier to understand and follow the logic of your code, without having to use complex and confusing callbacks or promises.

async/await and Fetch API

Together, async/await and the Fetch API both provide a powerful, efficient, and easy understand.

Async/await works by allowing developers to define functions as async and then use the await keyword to wait for the result of a promise to resolve.

Fetch API is designed to work with asynchronous operations, which allows making requests to an API or server without blocking the main thread of the application and always fetch() method returns a Promise.

By default, the fetch() method uses the GET method, then all the examples will use it.

Obviously, you can use Promises with the .then() and .catch() methods to handle the asynchronous response from the API:

    fetch("https://api.chucknorris.io/jokes/random")
      .then((response) => response.json())
      .then((data) => {
        // Request was successful
        console.log(data);
      })
      .catch((error) => {
        // If an error occurs, see the error in the console.
        console.error(error);
      });
Enter fullscreen mode Exit fullscreen mode

But when you use async/await is often considered a cleaner and more readable approach because it avoids nested callbacks and provides a more linear and readable code flow. The try/catch block is used to handle any errors that may occur during the API request or parsing of the response, which is similar to the .catch() method in the first example. However, it provides a more elegant way to handle errors, as any errors that occur during the try block will be caught in the catch block.

It is not obligatory to use async always in JavaScript, nor is it required to use try/catch in every situation. The choice between async and try/catch depends on the specific requirements and context of your code.

    try {
      const response = await fetch("https://api.chucknorris.io/jokes/random");
      const data = await response.json();

      // Request was successful
      console.log(data);
    } catch (error) {
      // If an error occurs, see the error in the console
      console.error(error);
    }
Enter fullscreen mode Exit fullscreen mode

Of course, readability is subjective and depends on the context of your code. However, in general, async/await can make your asynchronous code more readable and easier to understand, especially when working with complex or nested asynchronous operations.

If you want to fetch multiple API requests in parallel, you can use Promise combinators.

In the example below, I’m using Promise.all method to GET the JSONPlaceholder which is a free API whenever you need some fake data for testing and prototyping:

    // Create an array with 5 different todo url items.
    const todoList = Array.from({ length: 5 }).map(
      (_, number) => `https://jsonplaceholder.typicode.com/todos/${++number}`
    );

    // Fetch the 5 different todo items using Promise.all method and
    // return an array of responses and data.
    const fetchTodoList = async (urls) => {
      try {
        const responseList = await Promise.all(urls.map((url) => fetch(url)));
        const dataList = await Promise.all(
          responseList.map((response) => response.json())
        );
        console.log(dataList); // Output: array with 5 different todo object items.
      } catch (error) {
        console.error(error);
      }
    };

    // Create an array with 5 different todo object items.
    fetchTodoList(todoList);
Enter fullscreen mode Exit fullscreen mode

There’s one another good example using the async/await to get the categories list and category detail now using the Chuck Norris Jokes API:

    const getCategories = async () => {
      const response = await fetch("https://api.chucknorris.io/jokes/categories");
      const data = await response.json();
      return data;
    };

    const getCategory = async (category) => {
      const response = await fetch(
        `https://api.chucknorris.io/jokes/random?category=${category}`
      );
      const data = await response.json();
      return data;
    };

    const getCategoriesItems = async () => {
      const categories = await getCategories();

      for (const category of categories) {
        await getCategory(category);
      }
    };
Enter fullscreen mode Exit fullscreen mode

In summary, if you're dealing with asynchronous operations, using async/await along with try/catch can provide a powerful error handling mechanism. However, if you're working with synchronous code or don't need to pause the execution of code, try/catch alone can handle errors effectively. The choice between async/await and try/catch depends on the nature of your code and the requirements of error handling.

The combination of async/await and the Fetch API simplifies the process of handling HTTP requests and responses. With Fetch API, developers don’t have to deal with complex XHR objects or callbacks. The syntax for making a fetch request is straightforward, and it’s easy to extract the data from the response object.

async/await can simplify asynchronous programming in JavaScript, but it can also introduce additional complexity and potential issues. Always you need to be mindful of these potential issues and have a solid understanding of asynchronous programming, error handling, state management, performance considerations, and debugging when using async/await.

So, If you want to know more about Fetch API, please wait for the next article that I’ll publish.

Thank you for reading, I hope this article can somehow have increased your knowledge base about it.

Top comments (0)