React js Async and Await Practice Questions with Solutions

Introduction

async/await is a JavaScript feature that makes asynchronous code easier to read and manage. In React applications, it is commonly used when working with APIs, form submissions, and other asynchronous operations. You can use an async function with await to wait for a Promise to settle before continuing the function. In this chapter, we will solve practical questions covering async/await, API requests, error handling, multiple requests, and React state. React js Async and Await practice questions with solutions help to understand the concepts.

1. What is async/await in React?

async/await is not a React-specific feature. It is a JavaScript syntax used to work with Promises in a more readable way.

For example:

async function getData() {
  const response = await fetch(
    "https://example.com/api/users"
  );

  const data = await response.json();

  console.log(data);
}

Here:

  • async makes the function asynchronous.
  • await pauses that async function until the Promise settles.
  • The returned Promise from the async function can then be handled by the caller.

async/await is especially useful for API-related operations in React.


2. How do you use async/await with fetch() in React?

You can create an asynchronous function and use await with fetch().

async function fetchUsers() {
  const response = await fetch(
    "https://example.com/api/users"
  );

  const data = await response.json();

  console.log(data);
}

In a React component, this function can be called from an effect when the component needs to synchronize with an API.

import { useEffect } from "react";

function Users() {
  useEffect(() => {
    async function fetchUsers() {
      const response = await fetch(
        "https://example.com/api/users"
      );

      const data = await response.json();

      console.log(data);
    }

    fetchUsers();
  }, []);

  return <h1>Users</h1>;
}

The useEffect callback itself is kept synchronous; the asynchronous function is defined inside it.


3. What is the difference between async and await?

async and await have different purposes.

async

The async keyword makes a function return a Promise.

async function greet() {
  return "Hello";
}

The function returns a Promise that fulfills with "Hello".

await

await can be used inside an async function to wait for a Promise to settle.

async function getUser() {
  const response = await fetch(
    "https://example.com/api/user"
  );

  console.log(response);
}

A simple way to remember:

async → defines an asynchronous function

await → waits for a Promise inside that function


4. How do you handle errors with async/await in React?

You can use try...catch to handle errors.

async function fetchUsers() {
  try {
    const response = await fetch(
      "https://example.com/api/users"
    );

    if (!response.ok) {
      throw new Error("Failed to fetch users");
    }

    const data = await response.json();

    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

The try block contains the asynchronous operation.

If the request or JSON parsing fails, or if your code throws an error, the catch block can handle it.

Checking response.ok is important because fetch() does not reject its Promise merely because the server returns an HTTP error such as 404 or 500.


5. How do you use async/await with useState()?

You can use useState() to store data received from an asynchronous operation.

import { useEffect, useState } from "react";

function Users() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    async function fetchUsers() {
      try {
        const response = await fetch(
          "https://example.com/api/users"
        );

        if (!response.ok) {
          throw new Error("Failed to fetch users");
        }

        const data = await response.json();

        setUsers(data);
      } catch (error) {
        console.error(error);
      }
    }

    fetchUsers();
  }, []);

  return (
    <div>
      {users.map((user) => (
        <p key={user.id}>{user.name}</p>
      ))}
    </div>
  );
}

export default Users;

The API data is stored using:

setUsers(data);

When the state changes, React renders the updated user list.


6. How do you use async/await with Loading and Error States?

A practical application should usually show what is happening while an asynchronous request is in progress.

import { useEffect, useState } from "react";

function Products() {
  const [products, setProducts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");

  useEffect(() => {
    async function fetchProducts() {
      try {
        setLoading(true);
        setError("");

        const response = await fetch(
          "https://example.com/api/products"
        );

        if (!response.ok) {
          throw new Error("Failed to fetch products");
        }

        const data = await response.json();

        setProducts(data);
      } catch (error) {
        setError(error.message);
      } finally {
        setLoading(false);
      }
    }

    fetchProducts();
  }, []);

  if (loading) {
    return <h2>Loading...</h2>;
  }

  if (error) {
    return <h2>Error: {error}</h2>;
  }

  return (
    <div>
      {products.map((product) => (
        <p key={product.id}>{product.name}</p>
      ))}
    </div>
  );
}

export default Products;

Here:

try

handles the request,

catch

handles errors,

and:

finally

runs after the operation finishes, whether it succeeds or fails.

This makes the loading state easier to manage.


7. How do you fetch Data using async/await when a value changes?

Suppose you want to fetch user information based on a changing userId.

import { useEffect, useState } from "react";

function User({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    async function fetchUser() {
      try {
        const response = await fetch(
          `https://example.com/api/users/${userId}`
        );

        if (!response.ok) {
          throw new Error("Failed to fetch user");
        }

        const data = await response.json();

        setUser(data);
      } catch (error) {
        console.error(error);
      }
    }

    fetchUser();
  }, [userId]);

  if (!user) {
    return <p>Loading user...</p>;
  }

  return <h2>{user.name}</h2>;
}

export default User;

The dependency array contains:

[userId]

When userId changes, the effect runs again and requests the corresponding user data.


8. How do you make Multiple API Requests using async/await?

You can make multiple asynchronous requests sequentially.

async function fetchData() {
  const usersResponse = await fetch(
    "https://example.com/api/users"
  );

  const users = await usersResponse.json();

  const postsResponse = await fetch(
    "https://example.com/api/posts"
  );

  const posts = await postsResponse.json();

  console.log(users);
  console.log(posts);
}

The second request starts after the first request has completed.

If the requests are independent, you can often run them concurrently with Promise.all():

async function fetchData() {
  const [usersResponse, postsResponse] =
    await Promise.all([
      fetch("https://example.com/api/users"),
      fetch("https://example.com/api/posts")
    ]);

  if (!usersResponse.ok || !postsResponse.ok) {
    throw new Error("One or more requests failed");
  }

  const [users, posts] = await Promise.all([
    usersResponse.json(),
    postsResponse.json()
  ]);

  console.log(users);
  console.log(posts);
}

Promise.all() is useful when the requests do not depend on each other and you want them to run concurrently.


9. How do you send Form Data using async/await?

async/await can also be used when submitting data from a React form.

Example:

import { useState } from "react";

function ProductForm() {
  const [name, setName] = useState("");

  async function handleSubmit(event) {
    event.preventDefault();

    try {
      const response = await fetch(
        "https://example.com/api/products",
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json"
          },
          body: JSON.stringify({
            name: name
          })
        }
      );

      if (!response.ok) {
        throw new Error("Failed to create product");
      }

      const data = await response.json();

      console.log(data);
    } catch (error) {
      console.error(error);
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={name}
        onChange={(event) => setName(event.target.value)}
      />

      <button type="submit">
        Add Product
      </button>
    </form>
  );
}

export default ProductForm;

The sequence is:

Form Submission
      ↓
Prevent Default
      ↓
POST Request
      ↓
await Response
      ↓
Parse JSON
      ↓
Update UI / Show Result


10. How do you Build a Practical API Fetching Component using async/await?

Let’s create a complete component using:

  • async/await
  • useEffect()
  • useState()
  • Loading state
  • Error handling
  • AbortController
import { useEffect, useState } from "react";

function Users() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");

  useEffect(() => {
    const controller = new AbortController();

    async function fetchUsers() {
      try {
        setLoading(true);
        setError("");

        const response = await fetch(
          "https://example.com/api/users",
          {
            signal: controller.signal
          }
        );

        if (!response.ok) {
          throw new Error("Failed to fetch users");
        }

        const data = await response.json();

        setUsers(data);
      } catch (error) {
        if (error.name !== "AbortError") {
          setError(error.message);
        }
      } finally {
        setLoading(false);
      }
    }

    fetchUsers();

    return () => {
      controller.abort();
    };
  }, []);

  if (loading) {
    return <h2>Loading users...</h2>;
  }

  if (error) {
    return <h2>Error: {error}</h2>;
  }

  return (
    <div>
      <h1>User List</h1>

      {users.length === 0 ? (
        <p>No users found.</p>
      ) : (
        users.map((user) => (
          <div key={user.id}>
            <h3>{user.name}</h3>
            <p>{user.email}</p>
          </div>
        ))
      )}
    </div>
  );
}

export default Users;

How this example works

First, the component creates state:

const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");

The API request is performed inside an asynchronous function:

async function fetchUsers() {
  ...
}

The request waits for the response:

const response = await fetch(...);

Then the response is converted into JSON:

const data = await response.json();

Finally, the data is stored:

setUsers(data);

If an error occurs, it is stored in:

setError(error.message);

The AbortController allows the request to be canceled when the effect is cleaned up.

This is a practical pattern for basic API fetching with async/await in React.

Key Takeaways

  • async/await is a JavaScript feature, not a React-specific feature.
  • async makes a function return a Promise.
  • await waits for a Promise to settle inside an async function.
  • async/await can make API code easier to read.
  • Use try...catch to handle errors from asynchronous operations.
  • Check response.ok because HTTP errors do not automatically reject fetch().
  • Store fetched data in state when it affects the UI.
  • Use useEffect() when the request needs to synchronize with an external system.
  • Use dependencies when the request depends on changing props or state.
  • Promise.all() can run independent requests concurrently.
  • AbortController can be used to cancel an in-progress fetch request.
  • Do not make the useEffect callback itself async; define the async function inside the effect instead.

FAQs

1. What is async/await in React?

async/await is JavaScript syntax used to work with Promises. React applications commonly use it for API requests and other asynchronous operations.

2. Can async/await be used with fetch() in React?

Yes. For example:

const response = await fetch("/api/users");
const data = await response.json();

3. Can useEffect() be declared async?

It is generally not recommended to make the useEffect callback itself async.

Instead, define an async function inside the effect:

useEffect(() => {
  async function fetchData() {
    // asynchronous code
  }

  fetchData();
}, []);

This keeps the effect callback’s return value compatible with React’s effect cleanup mechanism.

4. How do you handle errors with async/await?

Use try...catch.

try {
  const response = await fetch("/api/users");

  if (!response.ok) {
    throw new Error("Request failed");
  }

  const data = await response.json();
} catch (error) {
  console.error(error);
}

5. What does await do?

await waits for a Promise to settle before continuing the surrounding async function.

const response = await fetch("/api/users");

The async function resumes after the Promise settles.

6. Can async/await be used for POST requests?

Yes. You can use async/await with fetch() for POST requests.

const response = await fetch("/api/products", {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify(product)
});

7. What is Promise.all() used for?

Promise.all() waits for multiple Promises and fulfills when all of them fulfill.

For example:

const [users, posts] = await Promise.all([
  fetchUsers(),
  fetchPosts()
]);

It is useful when multiple asynchronous operations are independent and can run concurrently.

Written by Shubhranshu Shekhar, who has trained 20000+ students in coding.

Scroll to Top