Chapter 12 β€” Promises & Promise.all

πŸ“– Definition

A Promise is an object representing the eventual completion or failure of an asynchronous operation, with three possible states: pending, fulfilled, rejected.

πŸ” Explanation

A Promise transitions exactly once: from pending β†’ fulfilled (with a value) or pending β†’ rejected (with a reason). Once settled, it can't change.

Handlers

  • .then(onFulfilled, onRejected) β€” register success/failure handlers.
  • .catch(fn) β€” short for .then(undefined, fn).
  • .finally(fn) β€” always runs (cleanup).

Static Methods

Method Resolves when… Rejects when…
Promise.all all resolve any rejects (fails fast)
Promise.allSettled all settle (never rejects) β€”
Promise.race first settles (any outcome) first settles (any outcome)
Promise.any first fulfilled all reject (AggregateError)

πŸ’» Code Example β€” Creating a Promise

function wait(ms) {
  return new Promise((resolve, reject) => {
    if (ms < 0) return reject(new Error("Negative ms"));
    setTimeout(resolve, ms);
  });
}

wait(1000).then(() => console.log("1 second later"));

πŸ’» Code Example β€” Chaining

fetchUser(1)
  .then(user => fetchOrders(user.id))
  .then(orders => orders.filter(o => o.status === "paid"))
  .then(paid => console.log("Paid orders:", paid))
  .catch(err => console.error("Something failed:", err))
  .finally(() => console.log("Done"));

Each .then returns a new Promise β€” the next handler receives whatever the previous one returned.

πŸ’» Code Example β€” async/await

async function loadDashboard(userId) {
  try {
    const user = await fetchUser(userId);
    const orders = await fetchOrders(user.id);
    return { user, orders };
  } catch (err) {
    console.error("Failed to load dashboard:", err);
    throw err;
  }
}

βœ… async functions always return a Promise. await pauses inside the async function only, not the whole program.

πŸ’» Code Example β€” Promise.all (Parallel)

async function loadDashboard(userId) {
  // All three start at the same time β€” total time β‰ˆ slowest one
  const [user, orders, notifications] = await Promise.all([
    fetchUser(userId),
    fetchOrders(userId),
    fetchNotifications(userId),
  ]);
  return { user, orders, notifications };
}

⚠️ Common Mistake β€” Sequential Awaits

// ❌ Slow β€” takes the SUM of all three
const user = await fetchUser(id);
const orders = await fetchOrders(id);
const notifs = await fetchNotifications(id);

// βœ… Fast β€” takes the MAX of all three
const [user, orders, notifs] = await Promise.all([
  fetchUser(id), fetchOrders(id), fetchNotifications(id),
]);

πŸ’» Code Example β€” Promise.allSettled

const results = await Promise.allSettled([
  fetchUser(1),       // resolves
  fetchUser(999),     // rejects (not found)
  fetchUser(2),       // resolves
]);

results.forEach((r, i) => {
  if (r.status === "fulfilled") console.log(i, "βœ“", r.value);
  else                          console.log(i, "βœ—", r.reason);
});

Use when you want all results, even if some fail.

πŸ’» Code Example β€” Promise.race

function timeout(ms) {
  return new Promise((_, reject) =>
    setTimeout(() => reject(new Error("Timeout")), ms)
  );
}

await Promise.race([
  fetch("/api/data"),
  timeout(3000),        // rejects after 3s
]);

πŸ’» Code Example β€” Promise.any

// First successful CDN wins
await Promise.any([
  fetch("https://cdn1.example.com/data"),
  fetch("https://cdn2.example.com/data"),
  fetch("https://cdn3.example.com/data"),
]);

πŸ’» Code Example β€” Error Handling Strategies

// Strategy 1: catch on each call to not stop the batch
const results = await Promise.all(
  ids.map(id =>
    fetchUser(id).catch(err => ({ id, error: err.message }))
  )
);

// Strategy 2: use allSettled
const settled = await Promise.allSettled(ids.map(fetchUser));

πŸ’» Code Example β€” Microtask vs Macrotask

console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");

// Output: 1, 4, 3, 2
// Promises (microtasks) run BEFORE setTimeout (macrotasks).

For a visual, step-by-step explanation of why this happens, see Chapter 10 β€” Event Loop & Task Queues.

🌍 Real-World Impact

  • API parallelization in dashboards (Promise.all saves 60–80% of load time).
  • Retries with backoff use Promises chained recursively.
  • Timeouts wrap fetches with Promise.race.

🎯 Likely Interview Questions

  1. What is a Promise?
  2. Difference between Promise.all and Promise.allSettled?
  3. What is async/await? Is it the same as Promises? β€” Sugar over Promises. async always returns a Promise.
  4. How do you handle one failing promise without killing the whole batch?
  5. Output prediction: see microtask example above.

← Array Methods | Index | Next: Event Loop & Task Queues β†’