Topic 10 / 15

Asynchronous JavaScript — Promises & async/await

~12 min read  //  JavaScript Series  //  Coding India

Why Async Exists

JavaScript runs on one thread. If a network request blocked it, the entire page would freeze — no clicks, no scrolling. So slow operations (network, timers, file access) run in the background and notify you when done. The event loop coordinates this.

console.log("1");
setTimeout(() => console.log("2"), 0);   // even 0ms goes to the queue
console.log("3");
// prints 1, 3, 2 — async callbacks wait for the current code to finish

Promises

A Promise is an object representing a future value — pending, then fulfilled or rejected:

fetch("/api/courses")                 // returns a Promise immediately
  .then(response => response.json()) // runs when fulfilled
  .then(data => console.log(data))
  .catch(error => console.error(error))
  .finally(() => hideSpinner());

Before Promises, code nested callbacks five levels deep (“callback hell”). Promises flattened it; async/await made it read like normal code.

async / await — the Way You’ll Actually Write It

async function loadCourses() {
  try {
    const response = await fetch("/api/courses");
    if (!response.ok) {                       // fetch does NOT reject on 404/500!
      throw new Error(`HTTP ${response.status}`);
    }
    const data = await response.json();
    render(data);
  } catch (error) {
    showError(error.message);                 // network errors land here too
  } finally {
    hideSpinner();
  }
}
  • await pauses this function (not the page) until the Promise settles.
  • await only works inside async functions (or at module top level).
  • An async function always returns a Promise.

Parallel, Not Sequential

The classic performance bug — awaiting in series when requests are independent:

// SLOW — second request waits for the first (600ms total)
const users = await fetchUsers();      // 300ms
const posts = await fetchPosts();      // 300ms

// FAST — both in flight at once (300ms total)
const [users, posts] = await Promise.all([
  fetchUsers(),
  fetchPosts(),
]);

Variants: Promise.allSettled() never rejects — you get every result with its status; Promise.race() resolves with the first to finish.

Making Your Own Promises

Mostly you consume Promises, but wrapping callback APIs is worth knowing:

const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

await sleep(1000);     // pause 1 second
console.log("one second later");