Skip to content
Go back

Why Does JavaScript Fetch Need Two Awaits?

Published:  at  05:30 AM

If you’ve worked with the Fetch API in JavaScript, you’ve probably seen this pattern:

const response = await fetch(url);
const data = await response.json();

At first glance, it seems like we’re doing two separate awaits for one operation. So what’s going on under the hood?

Let’s break it down.


🚀 The Two Awaits Explained

1. await fetch(url)

This sends the request and waits for the HTTP response headers to arrive.

This gives you a Response object, which you can inspect before deciding what to do next.


2. await response.json() (or .text(), .blob(), etc.)

This reads and parses the response body.


🧠 Why Are They Separate?

This design is intentional. The Fetch API gives you:

Example:

const response = await fetch(url);

if (!response.ok) {
  throw new Error(`HTTP error! status: ${response.status}`);
}

const data = await response.json(); // Only parse if OK

🤔 What If You Skip response.json()?

Great question.

If you only do:

const response = await fetch(url);

And don’t follow up with .json() or .text():

🤔 How to verify this?

You can check by opening the network tab. You will see a blank screen in the response tab.

🤔 How to Cancel/Close a Fetch Response (If You Don’t Want the Body)

✅ Option 1: Use response.body.cancel()

const response = await fetch(url);

if (!response.ok) {
  // Cancel the stream if you're not going to read it
  response.body?.cancel();
  throw new Error(`Request failed with status ${response.status}`);
}

✅ Option 2: Use AbortController to Cancel Entire Request

const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);

try {
  const response = await fetch(url, { signal: controller.signal });

  if (!response.ok) {
    response.body?.cancel();
    throw new Error(`Error: ${response.status}`);
  }

  const data = await response.json();
  clearTimeout(timeoutId);
  console.log(data);
} catch (err) {
  console.error("Fetch aborted or failed", err);
}

✅ Quick Summary

ActionBody Downloaded?Body Parsed?
await fetch(url)❌ (just headers)
await response.json()✅ (as JSON)
await response.text()✅ (as text)
No body read at all

🏁 Conclusion

The two-step await fetch() + await response.json() pattern is by design. It provides flexibility, improves efficiency, and avoids unnecessary processing when you don’t need the response body.

Next time you use fetch, you’ll know exactly what’s happening—and when!


Suggest Changes

Previous Post
How `Localhost` Works?
Next Post
How to Integrate Chargebee Payment Links in Your Notion Website