JavaScript Interview Prep
Asynchronous JavaScript

Promise Combinators

all, race, allSettled, any

LinkedIn Hook

Four static methods on the Promise constructor — all, race, allSettled, any — decide whether your dashboard loads all-or-nothing, survives a flaky API, times out a slow request, or gracefully fails over to a backup CDN.

Most developers only know Promise.all, and that's the one that hurts them in production. Promise.all short-circuits on the first rejection, which means one broken endpoint takes down the whole screen. Promise.allSettled would have rendered the other three widgets and logged the failure. Promise.race would have given you a clean timeout pattern in three lines. Promise.any would have picked the fastest working CDN automatically.

In this lesson you'll learn the exact semantics of each combinator, the one-word difference between race and any, what an AggregateError is, and the copy-paste timeout pattern every backend engineer should have in their toolbox.

Pick the wrong combinator and your entire page breaks over one bad request. Pick the right one and it self-heals.

Read the full lesson -> [link]

#JavaScript #InterviewPrep #Promises #AsyncJS #Frontend #CodingInterview #WebDevelopment


Promise Combinators thumbnail


What You'll Learn

  • The four Promise combinators and when each one is the correct choice
  • How to build a timeout wrapper with Promise.race and pick the fastest CDN with Promise.any
  • What an AggregateError is and why it only appears with Promise.any

The Four Combinators at a Glance

JavaScript provides four static methods on the Promise constructor for handling multiple Promises. Think of them like different rules for a group project:

MethodResolves WhenRejects WhenUse Case
Promise.allALL fulfillANY ONE rejectsNeed all results, fail fast
Promise.raceFirst to settle (either way)First to settle (if rejected)Timeout pattern, fastest response
Promise.allSettledALL settle (regardless of outcome)Never rejectsNeed all results, even failures
Promise.anyFirst to FULFILLALL reject (AggregateError)Fastest successful response

Promise.all — All or Nothing

// Simulating API calls with different response times
const fetchUser = () =>
  new Promise((resolve) => setTimeout(() => resolve({ name: "Rakibul" }), 300));
const fetchPosts = () =>
  new Promise((resolve) => setTimeout(() => resolve(["Post 1", "Post 2"]), 500));
const fetchNotifications = () =>
  new Promise((resolve) => setTimeout(() => resolve(5), 200));

// All three run in parallel, result arrives when ALL are done (~500ms)
async function loadDashboard() {
  try {
    const [user, posts, notifications] = await Promise.all([
      fetchUser(),
      fetchPosts(),
      fetchNotifications(),
    ]);
    console.log(user);          // { name: "Rakibul" }
    console.log(posts);         // ["Post 1", "Post 2"]
    console.log(notifications); // 5
  } catch (error) {
    console.error("One failed, all failed:", error);
  }
}

Promise.all with One Failure (and allSettled to the Rescue)

const success1 = () => Promise.resolve("Data A");
const failure = () => Promise.reject(new Error("API down!"));
const success2 = () => Promise.resolve("Data C");

// Promise.all — one failure kills everything
Promise.all([success1(), failure(), success2()])
  .then((results) => console.log(results))       // never runs
  .catch((err) => console.error(err.message));    // "API down!"

// Promise.allSettled — gets ALL results regardless
Promise.allSettled([success1(), failure(), success2()])
  .then((results) => {
    console.log(results);
    // [
    //   { status: "fulfilled", value: "Data A" },
    //   { status: "rejected",  reason: Error("API down!") },
    //   { status: "fulfilled", value: "Data C" }
    // ]

    // Filter successes and failures separately
    const successes = results.filter((r) => r.status === "fulfilled");
    const failures = results.filter((r) => r.status === "rejected");
    console.log(`${successes.length} succeeded, ${failures.length} failed`);
  });

Promise.race — The Timeout Pattern

Promise.race resolves or rejects with whichever Promise settles first. The classic use case is implementing a timeout:

function fetchWithTimeout(url, timeoutMs) {
  const fetchPromise = fetch(url);

  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => reject(new Error(`Request timed out after ${timeoutMs}ms`)), timeoutMs);
  });

  // Whichever settles first wins
  return Promise.race([fetchPromise, timeoutPromise]);
}

// Usage
async function getData() {
  try {
    const response = await fetchWithTimeout("https://api.example.com/data", 3000);
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error.message); // "Request timed out after 3000ms" or network error
  }
}

Promise.any — First Success Wins

// Try multiple CDN mirrors — use whichever responds first successfully
async function loadScript() {
  try {
    const fastest = await Promise.any([
      fetch("https://cdn1.example.com/lib.js"),
      fetch("https://cdn2.example.com/lib.js"),
      fetch("https://cdn3.example.com/lib.js"),
    ]);
    console.log("Loaded from fastest CDN:", fastest.url);
  } catch (error) {
    // Only reaches here if ALL three fail
    console.error("All CDNs failed:", error); // AggregateError
    console.log(error.errors); // array of individual errors
  }
}

Promise.any vs Promise.race — The Key Difference

const slow = new Promise((resolve) => setTimeout(() => resolve("slow"), 500));
const fastReject = new Promise((_, reject) => setTimeout(() => reject("fail"), 100));

// Promise.race: first to SETTLE wins (even if it's a rejection)
Promise.race([slow, fastReject])
  .then((val) => console.log("race resolved:", val))
  .catch((err) => console.log("race rejected:", err)); // "race rejected: fail"

// Promise.any: first to FULFILL wins (rejections are ignored until all fail)
Promise.any([slow, fastReject])
  .then((val) => console.log("any resolved:", val))   // "any resolved: slow"
  .catch((err) => console.log("any rejected:", err));

The one-word difference: race watches for the first settlement, any watches for the first fulfillment.

Promise Combinators visual 1


Common Mistakes

  • Reaching for Promise.all when you actually need Promise.allSettled — one unreliable endpoint then tanks the entire batch even though the other results were fine.
  • Confusing Promise.race and Promise.anyrace also rejects if the first settler is a rejection, so using it for "first success" quietly fails on a fast-failing Promise.
  • Forgetting that Promise.any rejects with an AggregateError (not a single Error) — err.message is generic; the real reasons live on err.errors.

Interview Questions

Q: What is the difference between Promise.all and Promise.allSettled?

Promise.all short-circuits on the first rejection — if any Promise fails, the whole thing rejects. Promise.allSettled waits for every Promise to settle and returns an array of {status, value/reason} objects, never rejecting. Use allSettled when you need results from all operations even if some fail.

Q: Explain Promise.race with a practical use case.

Promise.race resolves or rejects with whichever Promise settles first. The most common use case is a timeout: race your actual fetch against a setTimeout that rejects. Whichever completes first determines the outcome.

Q: What is the difference between Promise.any and Promise.race?

Promise.race settles with the first Promise to settle, whether fulfilled or rejected. Promise.any waits for the first fulfillment, ignoring rejections. Promise.any only rejects if ALL Promises reject, throwing an AggregateError containing all the rejection reasons.

Q: What is an AggregateError?

An error type thrown when Promise.any rejects (all Promises failed). It has an .errors property — an array containing every individual rejection reason.

Q: How would you implement a timeout using Promises?

Race the real operation against a Promise that rejects via setTimeout: Promise.race([realPromise, new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), ms))]). Whichever settles first wins.


Quick Reference — Cheat Sheet

PROMISE COMBINATORS — QUICK MAP

+-----------------+----------------+-----------------------+
| Method          | Resolves       | Rejects                |
+-----------------+----------------+-----------------------+
| Promise.all     | All fulfill    | Any one rejects       |
| Promise.race    | First settles  | First settles (if rej)|
| Promise.allSet. | All settle     | Never                 |
| Promise.any     | First fulfills | All reject (Aggregate)|
+-----------------+----------------+-----------------------+

Picking one:
  all        -> need every result, fail fast
  allSettled -> need every result, tolerate failures
  race       -> timeouts, first-to-finish wins
  any        -> fallback to first working source

Timeout pattern:
  Promise.race([
    realPromise,
    new Promise((_, rej) =>
      setTimeout(() => rej(new Error("timeout")), ms))
  ])

AggregateError:
  Thrown ONLY by Promise.any when all reject.
  Access individual reasons via err.errors (array).

Previous: async/await — Syntactic Sugar Over Promises Next: Error Handling in Async Code


This is Lesson 3.9 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.

On this page