Back to list
ReactasyncawaitAPIJavaScript

[React][API] Understand async/await and Build Async Requests

Stop logging Promise pending after fetch - declare async functions, pause with await until data arrives, and replace nested .then chains with readable top-to-bottom code.

1. Environment

  • OS: Windows 11 / macOS
  • Language: JavaScript (ES8+)
  • Frontend library: React.js
  • IDE: Visual Studio Code (VS Code)

2. Problem

Suppose you fetch user data from a server and show it on screen.

JavaScript is impatient by nature. It fires fetch, then runs the next line (console.log) without waiting for the response.

You end up with undefined or Promise { <pending> } instead of real data.

// Failing code (treated as if it were synchronous)
function getUserInfo() {
  // 1. Ask the server (takes ~1s)
  const data = fetch('https://api.example.com/user');

  // 2. Log before data arrives!
  console.log(data);
  // Result: undefined or Promise { <pending> }
}

The syntax that tells JavaScript “do not move to the next line until the server answers|–is async / await.

Related: Promise on every API call

3. Cause

async - “this function is asynchronous|–

  • Where: In front of a function declaration
  • Role: Marks a normal function as async. An async function always returns a Promise.
  • Rule: You may use await only inside a function declared with async.

await - “pause here until the data arrives|–

  • Where: Immediately before a slow Promise-returning call (API, etc.)
  • Role: Suspends that line until the response is ready, then continues.
  • Rule: Cannot stand alone - must live inside an async function.

4. Solution

1) Old .then chains vs modern async / await

Past: nested .then (callback hell)

function fetchUserData() {
  fetch('https://api.example.com/user')
    .then((response) => response.json())
    .then((user) => {
      fetch(`https://api.example.com/posts/${user.id}`)
        .then((response) => response.json())
        .then((posts) => {
          console.log('Fetched data:', posts);
        });
    });
}

Nested callbacks dig deeper and deeper - hard to read.

Modern: async / await

Frontend practice almost always prefers this shape. The code reads top-to-bottom like synchronous code:

// 1. Mark the function as async
async function fetchUserData() {
  try {
    // 2. Pause until the response arrives
    const userResponse = await fetch('https://api.example.com/user');
    const user = await userResponse.json();

    // 3. Next line runs only after the above finishes
    const postResponse = await fetch(`https://api.example.com/posts/${user.id}`);
    const posts = await postResponse.json();

    console.log('Fetched data:', posts);
  } catch (error) {
    // try/catch keeps error handling tidy
    console.error('Failed to fetch data.', error);
  }
}

5. Summary

For React/JavaScript API work, remember the formula: open with async, wait with await.

Interview tip

Q: From the client - server line of code, await looks synchronous - why call it async?

A: From the browser?’s point of view it is still asynchronous: the UI does not freeze while you wait. Frontend work is judged from that browser perspective, so the name async is correct.

Related posts