Promise APIs + Interview Questions: all, allSettled, race, and any
When building modern JavaScript and modern framework applications (such as React), handling multiple asynchronous operations concurrently is a fundamental requirement. Whether fetching user profiles in parallel or rendering dashboard metrics, JavaScript provides four primary Promise APIs: Promise.all(), Promise.allSettled(), Promise.race(), and Promise.any().
Understanding the precise mechanics, state transitions, timing, and edge-case behavior of these four methods is critical both for day-to-day engineering and high-stakes technical interviews.
1. The Problem: Handling Parallel Asynchronous Operations
When an application needs to fetch data for multiple independent resources—such as requesting user profile data for 10 distinct user IDs—making these network calls sequentially creates an unnecessary waterfall delay. Instead, these operations should be triggered concurrently in parallel.
JavaScript offers built-in Promise static methods (Promise APIs) to manage, coordinate, and aggregate multiple parallel asynchronous tasks depending on the failure tolerance and response criteria required by the application.
2. Essential Promise Terminology & Life-Cycle States
To reason effectively about Promise APIs—especially during technical interviews—you must use precise industry terminology:
- Settled: A promise has completed its lifecycle execution. It is no longer pending. A settled promise can be either fulfilled or rejected.
- Fulfilled / Resolved: The asynchronous operation completed successfully, yielding a result value.
- Rejected: The asynchronous operation failed, yielding an error or rejection reason.
Key Takeaway: "Settled" is an umbrella term for a promise that has reached its final state, regardless of whether it succeeded or failed.
3. Promise.all() — The Fail-Fast Parallel Aggregator
Mental Model & Contract
Promise.all() accepts an iterable (typically an array) of promises as input and returns a single combined Promise.
Promise.all([P1, P2, P3])
It is designed for operations that depend on all sub-tasks succeeding together ("all-or-nothing").
Success Scenario
If all input promises fulfill successfully, Promise.all() returns an array containing the resolved values of every promise in the exact order they were passed into the array.
- Execution Timing: It executes all promises concurrently and waits for the slowest promise to complete.
- Example:
P1fulfills in3swithVal1P2fulfills in1swithVal2P3fulfills in2swithVal3
Output: [Val1, Val2, Val3] returned after 3 seconds.
Failure Scenario ("Fail-Fast")
If any promise in the array rejects, Promise.all() immediately rejects with the error of that specific rejected promise.
- Behavior: It does not wait for remaining pending promises to finish. It aborts listening and rejects immediately upon the first failure.
- Example:
P1takes3sP2rejects after1swithError2P3takes2s
Output: Throws Error2 after 1 second.
Note on Promise Cancellation: When
Promise.all()rejects early due to a single failure, the remaining pending operations (P1andP3) are not cancelled. In standard JavaScript, executing promises cannot be cancelled midway; they will continue to run in the background until settled, butPromise.all()ignores their outcomes.
4. Promise.allSettled() — Safe Resilient Aggregation
Mental Model
While Promise.all() fails fast, Promise.allSettled() is designed for resilient workflows where you want to gather the outcome of every task regardless of individual successes or failures.
Behavior & Output Format
Promise.allSettled() waits for all input promises to reach a settled state (fulfilled or rejected). It never rejects early due to an error.
- Execution Timing: Always takes as long as the slowest promise in the array.
- Example:
P1fulfills in3swithVal1P2rejects in1swithError2P3fulfills in2swithVal3
Output: Returned after 3 seconds as an array of outcome objects:
[
{ status: "fulfilled", value: "Val1" },
{ status: "rejected", reason: "Error2" },
{ status: "fulfilled", value: "Val3" }
]
Real-World Analogy
Imagine rendering a dashboard containing 5 dynamic card widgets populated by 5 independent API calls:
- Using
Promise.all(): If 1 API fails, the entire dashboard crashes and shows an error page. - Using
Promise.allSettled(): If 1 API fails, 4 widgets render normally while 1 widget shows a localized fallback error state.
5. Promise.race() — The Speed-First Settled State
Mental Model
Promise.race() acts as a competitive race among input promises. It settles as soon as the first promise in the iterable settles (whether it fulfills or rejects).
Behavior
- Success First: Returns the value of the first promise to fulfill if it finishes before any rejections occur.
- Failure First: Returns/throws the error of the first promise to reject if it fails before any fulfillments occur.
Scenarios
-
First Settled is Success:
P1takes3sP2fulfills in1swithVal2P3takes2s- Output:
Val2after 1 second.
-
First Settled is Failure:
P1takes3sP2rejects in1swithError2P3takes2s- Output: Throws
Error2after 1 second.
6. Promise.any() — The Success-Seeking Race
Mental Model
Promise.any() is a success-seeking variant of a race. It returns the result of the first fulfilled (successful) promise, ignoring any rejections that occur prior to that first success.
Behavior
- It filters out failures and waits specifically for a success.
- Example with Partial Failure:
P1fulfills in3swithVal1P2rejects in1swithError2(Ignored)P3fulfills in2swithVal3
Output: Val3 after 2 seconds (ignoring P2's failure at 1s and ignoring P1 taking 3s).
7. Handling AggregateError in Promise.any()
What Happens When All Promises Reject?
If every promise passed to Promise.any() rejects, it has no success to return. In this edge case, Promise.any() waits until all promises have rejected and then throws a specialized error called an AggregateError.
Accessing Individual Errors
An AggregateError groups all individual rejection reasons into an internal .errors array property.
try {
const result = await Promise.any([p1, p2, p3]);
} catch (err) {
console.log(err); // Output: AggregateError: All promises were rejected
console.log(err.errors); // Output: ["Error from P1", "Error from P2", "Error from P3"]
}
8. Comprehensive Comparison Table of Promise APIs
9. Code Walkthrough & Practical Demonstrations
To demonstrate these APIs predictably without network variance, we define dynamic dummy promises using helper timers:
// Helper dummy promises with controlled completion times
const createPromise = (time, value, shouldFail = false) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldFail) {
reject(`Failed: ${value}`);
} else {
resolve(`Success: ${value}`);
}
}, time);
});
};
const p1 = createPromise(3000, "P1");
const p2 = createPromise(1000, "P2");
const p3 = createPromise(2000, "P3");
Demo 1: Promise.all() Execution
// Success Case
Promise.all([p1, p2, p3])
.then((res) => console.log("Promise.all Success:", res))
.catch((err) => console.error("Promise.all Error:", err));
// Console output after 3 seconds:
// Promise.all Success: ["Success: P1", "Success: P2", "Success: P3"]
// Failure Case (Fail-Fast)
const p2Fails = createPromise(1000, "P2", true);
Promise.all([p1, p2Fails, p3])
.then((res) => console.log(res))
.catch((err) => console.error("Promise.all Error:", err));
// Console output after 1 second:
// Promise.all Error: Failed: P2
Demo 2: Promise.allSettled() Execution
const p2Fails = createPromise(1000, "P2", true);
Promise.allSettled([p1, p2Fails, p3])
.then((res) => console.log("Promise.allSettled Results:", res));
/* Console output after 3 seconds:
Promise.allSettled Results: [
{ status: "fulfilled", value: "Success: P1" },
{ status: "rejected", reason: "Failed: P2" },
{ status: "fulfilled", value: "Success: P3" }
]
*/
Demo 3: Promise.race() Execution
const p2Fails = createPromise(1000, "P2", true);
// Fast Failure
Promise.race([p1, p2Fails, p3])
.then((res) => console.log("Race Winner:", res))
.catch((err) => console.error("Race Failure:", err));
// Console output after 1 second:
// Race Failure: Failed: P2
Demo 4: Promise.any() Execution & AggregateError
const p1Fails = createPromise(3000, "P1", true);
const p2Fails = createPromise(1000, "P2", true);
const p3Fails = createPromise(2000, "P3", true);
// Total Rejection Scenario
Promise.any([p1Fails, p2Fails, p3Fails])
.then((res) => console.log("First Success:", res))
.catch((err) => {
console.error("Main Error Message:", err);
console.error("Aggregated Rejection Reasons:", err.errors);
});
/* Console output after 3 seconds:
Main Error Message: AggregateError: All promises were rejected
Aggregated Rejection Reasons: ["Failed: P1", "Failed: P2", "Failed: P3"]
*/
10. Real-World Architectural Edge Cases & Interview Pitfalls
1. The Distinction Between "Settled" and "Fulfilled"
Interviewers often ask candidate developers to define Promise.race() vs Promise.any().
- Common Mistake: Stating that
Promise.race()returns the first successful promise. - Correct Response:
Promise.race()returns the first settled promise (regardless of pass/fail status).Promise.any()returns the first fulfilled promise (specifically seeking success).
2. Can Executing Promises Be Cancelled?
No. Once a Promise instance is initialized, its asynchronous callback logic is dispatched to the Web API container/event loop. Returning early from Promise.all() or Promise.race() does not stop the remaining underlying network calls or background tasks from running to completion.
3. Handling Output Data Structures
Always remember that Promise.all() returns an array of direct values, whereas Promise.allSettled() returns an array of wrapper objects containing metadata { status, value } or { status, reason }.
Summary and Key Takeaways
Promise.all()is an all-or-nothing aggregator that runs operations in parallel and fails fast on the very first rejection.Promise.allSettled()is a safe aggregator that waits for all tasks to complete regardless of failures, returning an array of status objects.Promise.race()returns the output of the first promise to settle (whether fulfilled or rejected).Promise.any()is a success-seeking race that returns the first fulfilled promise, ignoring failures unless all promises fail.AggregateErroris thrown byPromise.any()only when all input promises reject; individual errors are accessed via theerr.errorsproperty array.- Promise Non-Cancellation: Reaching an early result in
Promise.all()orPromise.race()does not cancel ongoing pending background promises. - Lingo Accuracy: Always differentiate clearly between Pending, Settled, Fulfilled, and Rejected states during technical explanations.