DEV Community

SAURAV KUMAR
SAURAV KUMAR

Posted on

Why Doesn’t My API Call Wait? — Understanding Promises & Async/Await in JavaScript

Imagine you’re building a small weather app 🌤️.

You click a “Get Weather” button, and your JavaScript calls a weather API like this:

const data = fetch("https://api.weatherapi.com/data");
console.log(data);

Enter fullscreen mode Exit fullscreen mode

You expect it to print the weather data.

Instead, it shows this 👇

Promise { <pending> }

Enter fullscreen mode Exit fullscreen mode

No temperature. No city name. Just... pending.

You try adding more logs, maybe even a delay — but the result doesn’t change.

Why?

Because JavaScript is asynchronous — it doesn’t stop and wait for slow tasks (like APIs, timers, or file reads).

While your fetch request is still in progress, JS moves to the next line.

To handle this properly, JavaScript gives us Promises — a way to represent “something that will finish later.”

And once you understand Promises, you’ll learn Async/Await, which makes asynchronous code look clean and readable — like synchronous code.


🧠 What Is a Promise?

A Promise is just a JavaScript object that represents the eventual result of an asynchronous operation.

It can be in one of three states:

  1. Pending → still waiting
  2. Fulfilled → operation succeeded
  3. Rejected → operation failed

Let’s see an example 👇

const weatherData = new Promise((resolve, reject) => {
  const success = true;
  if (success) {
    resolve("☀️ Weather data fetched successfully!");
  } else {
    reject("❌ Failed to fetch weather data!");
  }
});

weatherData
  .then((message) => console.log(message))
  .catch((error) => console.error(error));

Enter fullscreen mode Exit fullscreen mode

Step-by-step:

  1. We create a Promise that either resolves (success) or rejects (error).
  2. .then() handles success.
  3. .catch() handles failure.

This way, your app doesn’t freeze while waiting — it continues other work and handles the result later.


⚙️ The Problem with .then() Chains

As your app grows, chaining .then() becomes hard to read.

fetchUser()
  .then((user) => fetchWeather(user.city))
  .then((weather) => fetchForecast(weather.id))
  .catch((error) => console.error(error));

Enter fullscreen mode Exit fullscreen mode

This can quickly look messy — especially with multiple dependencies.

That’s where Async/Await comes in.


✨ Async/Await — Making Async Code Look Simple

async/await is just a cleaner way to work with Promises.

It helps you write asynchronous code that looks synchronous.

async function getWeather() {
  try {
    const response = await fetch("https://api.chucknorris.io/jokes/random");
    const data = await response.json();
    console.log("☀️ Weather:", data.value);
  } catch (error) {
    console.error("❌ Error fetching data:", error);
  }
}

getWeather();

Enter fullscreen mode Exit fullscreen mode

Step-by-step:

  1. Add the async keyword before the function.
  2. Use await to pause until the Promise resolves.
  3. Wrap code in try...catch for error handling.

🌦️ Mini Project: Weather Fetch App

Let’s build a beginner-friendly Weather App that uses async/await and proper error handling.

<body>
  <h1>🌦️ Weather App</h1>
  <input type="text" id="city" placeholder="Enter city name" />
  <button id="btn">Get Weather</button>
  <p id="result">Enter a city and click the button 👆</p>

  <script>
    const cityInput = document.getElementById("city");
    const btn = document.getElementById("btn");
    const result = document.getElementById("result");

    async function getWeather() {
      const city = cityInput.value.trim();
      if (!city) {
        result.innerText = "⚠️ Please enter a city name!";
        return;
      }

      try {
        result.innerText = "⏳ Loading...";
        const res = await fetch(`https://api.weatherapi.com/v1/current.json?key=YOUR_API_KEY&q=${city}`);
        if (!res.ok) throw new Error("City not found or API issue.");
        const data = await res.json();
        result.innerText = `🌤️ ${data.location.name}: ${data.current.temp_c}°C`;
      } catch (err) {
        result.innerText = "❌ Could not fetch weather data.";
        console.error(err);
      }
    }

    btn.addEventListener("click", getWeather);
  </script>
</body>

Enter fullscreen mode Exit fullscreen mode

What’s happening:

  • When you click the button, it calls getWeather().
  • The function waits (await) for the API to respond.
  • If something fails (no internet or bad city name), it’s handled gracefully with catch.

⚠️ Common Mistakes Beginners Make

  1. Forgetting to use await:
const data = fetch(url).json(); // ❌ Doesn't wait for the response  
Enter fullscreen mode Exit fullscreen mode
  1. Mixing .then() and await:
const data = await fetch(url).then(res => res.json()); // ❌ Avoid mixing  
Enter fullscreen mode Exit fullscreen mode
  1. No error handling: Always use try...catch to avoid unhandled rejections.
  2. Using await outside an async function: Only works inside functions marked with async.

🧩 Real-World Example in React

Here’s how you’d use the same concept inside a React component 👇

import { useEffect, useState } from "react";

export default function WeatherApp() {
  const [weather, setWeather] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchWeather() {
      try {
        const res = await fetch("https://api.weatherapi.com/v1/current.json?key=API_KEY&q=Delhi");
        const data = await res.json();
        setWeather(data.current.temp_c);
      } catch (err) {
        setError("Failed to fetch weather.");
      }
    }

    fetchWeather();
  }, []);

  if (error) return <p>{error}</p>;
  if (!weather) return <p>Loading...</p>;
  return <p>🌤️ Current Temperature: {weather}°C</p>;
}

Enter fullscreen mode Exit fullscreen mode

🎯 Bonus: How to Explain in an Interview

Q: What is a Promise?

A: A Promise represents a value that will be available in the future — success or failure.

Q: How is async/await different from Promises?

A: Async/Await is syntactic sugar over Promises — it makes async code cleaner and easier to read.

Q: How do you handle errors in async code?

A: Using try...catch blocks or the .catch() method.

Q: What happens if you forget await?

A: You get a Promise object instead of the resolved value.


🧠 Key Takeaways

✅ Promises represent future results of async operations.

✅ Async/Await makes Promises easier to use.

✅ Always handle errors using try...catch.

✅ Use async/await for cleaner, more readable code.

✅ Async doesn’t block — it helps your app stay responsive.


👋 About Me

Hi, I'm Saurav Kumar — a Software Engineer passionate about building modern web and mobile apps using JavaScript, TypeScript, React, Next.js, and React Native.

I’m exploring how AI tools can speed up development,

and I share beginner-friendly tutorials to help others grow faster.

🔗 Connect with me:

LinkedIn — I share short developer insights and learning tips

GitHub — Explore my open-source projects and experiments

If you found this helpful, share it with a friend learning JavaScript — it might help them too.

Until next time, keep coding and keep learning 🚀

Top comments (1)

Collapse
 
a-k-0047 profile image
ak0047

Thank you for sharing this article!
As a beginner with JavaScript, I found it really helpful and practical.
I'll keep these in mind.