Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 32

Fetching Data with Async/Await

Recommended resources

Hello! Let's get started with your next lesson.

In our last session, we explored the "why" behind asynchronous JavaScript. You learned that synchronous, or "blocking," code can freeze the user interface, and how the Event Loop, Web APIs, and the Callback Queue work together to run tasks like setTimeout without interrupting the user experience.

Today, we'll apply that foundational knowledge to a real-world scenario: fetching data from a server. Our learning outcome is to use the Fetch API with async/await to retrieve data from a public REST API. This is one of the most common and essential tasks in modern web development. You'll see how the principles of the Event Loop apply directly to fetch, and you'll learn a clean, modern syntax for handling the entire process.

This skill is the final piece of the puzzle you'll need for this module's mini-project: building a live weather widget.

1. From Callbacks to async/await

The fetch() function is a Web API, just like setTimeout. When you call it, the browser starts a network request in the background, and your JavaScript code can continue running. fetch() returns an object called a Promise, which is a placeholder for the future result of the request.

In the past, developers handled Promises using methods like .then() and .catch(), which could lead to nested chains of code that were hard to read. To solve this, modern JavaScript introduced the async/await syntax. It's "syntactic sugar" over Promises, allowing us to write asynchronous code that looks and reads almost like synchronous code.

Let's start with a clear, textual explanation of what the async and await keywords actually do.

Async/await - The Modern JavaScript Tutorial

This article from the highly-respected javascript.info tutorial is a fantastic reference for the fundamentals of async/await.

Read the first two sections, 'Async functions' and 'Await'. Focus on understanding these two key points: What does adding the async keyword to a function declaration do? What does the await keyword do when placed in front of a promise?

To summarize the key points from that reading:

  • An async function always returns a Promise.
  • The await keyword can only be used inside an async function. It pauses the function's execution until the Promise it's "awaiting" is settled (either resolved or rejected), and then it returns the result.

2. Making an API Call with fetch

Now let's see how these keywords work with the fetch function. When you fetch data, it's a two-step process:

  1. You initiate the network request and wait for the response headers. This initial response tells you if the request was successful, but it doesn't contain the actual data yet.
  2. You then need to parse the body of the response, for example, as a JSON object. This parsing step is also an asynchronous operation.

This means we will need to use await twice.

Master Async Await Javascript in an easy way

This video provides a concise demonstration of how to combine async, await, and fetch to get data from a public API. It clearly shows the two-step process.

Watch the segment from 00:59 to 02:04. Pay close attention to the two await keywords. The first await is for the network request itself, and the second is for parsing the response body into usable JSON data.

Here is the core pattern you just saw, which you will use frequently:

// A function to fetch user data from the JSONPlaceholder API
async function fetchUser() {
  // 1. Start the request and wait for the response object
  const response = await fetch('https://jsonplaceholder.typicode.com/users/1');

  // 2. Parse the response body as JSON and wait for the result
  const user = await response.json();

  // Now you can use the data!
  console.log(user);
}

// Call the function to execute it
fetchUser();

This code is much cleaner and easier to follow than nested .then() chains. It reads like a simple, synchronous set of instructions.


3. Handling Potential Errors

What happens if the network is down, or if we request a URL that doesn't exist? In programming, we can't assume everything will work perfectly. This is where error handling comes in. With async/await, the standard way to handle errors is with a try...catch block.

There's a very important nuance to how fetch handles errors that you need to be aware of.

How to Use Fetch with async/await

This article explains a critical detail about fetch and how to properly check for errors. Understanding this will prevent common bugs in your applications.

Read section 3, 'Handling fetch errors'. Focus on the difference between a network failure (which causes fetch to throw an error) and a bad HTTP status like 404 (which it does not). Understand why checking the response.ok property is so important.

As the article explains, fetch only fails if there's a network error. An HTTP error (like 404 Not Found or 500 Internal Server Error) is still considered a "successful" network request, so fetch won't automatically throw an error. We must check for this ourselves using the response.ok property.

Here is the robust, production-ready pattern for using fetch with async/await:

async function fetchData(url) {
  try {
    const response = await fetch(url);

    // Check if the response was successful (HTTP status 200-299)
    if (!response.ok) {
      // If not, create a new error with the status text to be caught below
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    const data = await response.json();
    console.log("Data received:", data);

  } catch (error) {
    // This block will catch both network errors and the error we threw above
    console.error("Could not fetch data:", error);
  }
}

// A valid request
fetchData('https://jsonplaceholder.typicode.com/posts/1');

// An invalid request that will result in a 404
fetchData('https://jsonplaceholder.typicode.com/posts/999999');

This pattern ensures you handle both network failures and bad server responses correctly.


4. Hands-On: Build a PokΓ©mon Fetcher

Now it's your turn to build something. You'll create a simple page that lets you type in a PokΓ©mon's name and fetch its picture from a public API. This will solidify everything we've just covered.

How to FETCH data from an API using JavaScript ↩️

Let's put theory into practice. This video walks through building a small application that fetches data from the PokΓ©API and displays it on the page. I encourage you to code along to get a real feel for the workflow.

Watch from 06:20 to 14:18. The first part (until 08:54) reviews the async/await and try/catch structure. The second part (from 08:54) shows how to connect this JavaScript logic to an HTML front-end. Follow along and build your own version.

To get you started, here is the basic HTML. Create an index.html and app.js file in a new project folder.

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>PokΓ©-Fetcher</title>
  <style>
    body { font-family: sans-serif; text-align: center; margin-top: 50px; }
    input { padding: 8px; }
    button { padding: 8px 12px; }
    img { margin-top: 20px; width: 150px; height: 150px; }
  </style>
</head>
<body>
  <h1>PokΓ©-Fetcher</h1>
  <input type="text" id="pokemonName" placeholder="Enter PokΓ©mon name">
  <button onclick="fetchData()">Fetch PokΓ©mon</button>
  <br>
  <img src="" alt="Pokemon Sprite" id="pokemonSprite" style="display: none;">

  <script src="app.js"></script>
</body>
</html>

Your task for app.js:
Write the fetchData function. It should:

  1. Be an async function.
  2. Use a try...catch block.
  3. Get the PokΓ©mon name from the input field.
  4. Fetch data from the API: https://pokeapi.co/api/v2/pokemon/{POKEMON_NAME}. Remember to make the name lowercase.
  5. Check if response.ok is true.
  6. Parse the JSON.
  7. Get the sprite URL from the data: data.sprites.front_default.
  8. Update the src of the <img> element and change its display style to block.
  9. In the catch block, log any errors to the console.

Give it a try! You can check your work against the code in the video.


Conclusion

Excellent work! You've successfully fetched data from a remote server using the most modern JavaScript syntax available. This workflow is fundamental to building dynamic, data-driven applications.

API Request Workflow with Fetch and Async/Await
This flowchart visualizes the entire process you just learned: making a request with `fetch` and `async/await`, handling errors, and finally using the data to update your application's UI.

Key Takeaways:

  • fetch() is the browser's built-in function for making network requests. It returns a Promise.
  • async/await is the modern syntax for handling Promises, making asynchronous code easier to read and write.
  • The standard fetch process requires two await calls: one for fetch() itself and one for parsing the response body (e.g., response.json()).
  • Robust error handling involves using a try...catch block and manually checking the response.ok property to handle bad HTTP statuses.

In our next lesson, we will focus on handling API errors gracefully. Instead of just logging an error to the console, you will learn how to display user-friendly messages on the page, which is crucial for a good user experience.