Promise APIs
Ever scrambled to manage several asynchronous tasks in JavaScript—like fetching data from different APIs all at once? JavaScript’s Promise APIs give you powerful tools to coordinate these operations, regardless of whether you’re waiting for everything, just need the first result, or want to know every outcome.
The four key APIs we’ll explore are:
Promise.all()
Promise.allSettled()
Promise.race()
Promise.any()
Each is suited for common, real-world async scenarios. Let’s see how they solve our toughest concurrency problems.
Promise.all()
: Wait Until Everything Succeeds
Problem: You need to fetch data for multiple users and only want to act when all calls finish successfully.
Solution: Use Promise.all()
.
- Input: An array (or iterable) of promises.
- Behavior: Waits for all promises to resolve.
- If all succeed: Resolves with an array containing the results of each promise, in the same order they were passed in.
- If any fail: Rejects immediately with that first error (others keep running, but their results are ignored). It doesn't wait for the others; it "fails fast."
- Use this when: Every task is critical before you can move on.
How Promise.all()
Handles Success
Let's say we have three promises: P1 (takes 3s), P2 (takes 1s), and P3 (takes 2s). All of them will eventually resolve successfully.
What Happens When a Promise Fails in Promise.all()
?
Now, what if one of those promises rejects? Let's use the same timings (P1: 3s, P2: 1s, P3: 2s), but this time, P2 rejects after 1 second.
Important Note
Even though Promise.all()
rejects early, the other promises (P1 and P3 in this case) will continue running until they complete. You just won't get their results via this Promise.all()
call. JavaScript doesn't provide a standard way to cancel a promise once it's started.
Promise.allSettled()
: Getting All Outcomes
Problem: What if you want to run multiple independent tasks and need a result for every one, whether it succeeded or failed?
Solution: Try Promise.allSettled()
.
- Input: An array (or iterable) of promises.
- Behavior: Waits for all promises to settle (resolve or reject).
- Output: Resolves with an array of objects:
- For fulfilled promises:
{ status: "fulfilled", value: <resolved_value> }
- For rejected promises:
{ status: "rejected", reason: <rejection_reason> }
- For fulfilled promises:
- Use Case: Ideal when you need to perform several independent tasks and want to know the result of each one, regardless of individual successes or failures.
How Promise.allSettled()
Handles Failure
Now, let's say P2 (1s) rejects, while P1 (3s) and P3 (2s) resolve.
Key Difference
Promise.all()
is "fail-fast," while Promise.allSettled()
waits for all results, success or failure.
Promise.race()
: The Fastest Result Wins
Problem: You have several possible data sources (like redundant servers) and want whichever responds first, no matter which.
Solution: Use Promise.race()
!
- Input: An array (or iterable) of promises.
- Behavior: Resolves or rejects as soon as any promise settles.
- If the first to settle resolves, so does
Promise.race()
. - If the first to settle rejects, so does
Promise.race()
.
- If the first to settle resolves, so does
- Use this when: You only care about the fastest result, regardless of success or failure.
How Promise.race()
Handles Success
Consider P1 (3s), P2 (1s), and P3 (2s), all resolving successfully.
How Promise.race()
Handles Failure
What if the fastest promise rejects? Let's use P1 (3s, resolves), P2 (1s, rejects), P3 (2s, resolves).
In Short
Promise.race()
mirrors the outcome of the very first promise to finish, regardless of whether it's a success or failure.
Promise.any()
: First Success (Ignoring Failures)
Problem: You only want the first successful result—even if some tasks fail. Only fail if all options do.
Solution: Choose Promise.any()
.
- Input: An array (or iterable) of promises.
- Behavior: Waits for the first promise to fulfill.
- Resolves with that result.
- Ignores all rejections unless every promise rejects.
- On total failure: Rejects with an
AggregateError
(containing all rejection reasons). - Use this when: You want any success, such as fallback or backup requests.
It's a success seeking race.
How Promise.any()
Handles Success
Let's use P1 (3s, resolves), P2 (1s, resolves), P3 (2s, resolves).
How Promise.any()
Handles Partial Failure
Now, what if the fastest promise fails? P1 (3s, resolves), P2 (1s, rejects), P3 (2s, resolves).
How Promise.any()
Handles Total Failure
What if all promises reject? P1 (3s, rejects), P2 (1s, rejects), P3 (2s, rejects).
Key Distinction
Promise.race()
settles with the first settled promise (success or failure). Promise.any()
settles with the first fulfilled (successful) promise, only rejecting if all promises reject.
Code Examples: Promise.all()
Let's see Promise.all()
in action. We'll create three promises with different resolution times.
Case 1: All Promises Resolve Successfully
Case 2: One Promise Rejects (Fail-Fast)
Now, let's make promise2
reject.
Important Note
Always add a .catch()
block when using Promise.all()
to handle potential rejections gracefully. Relying on uncaught error handlers in the browser is not recommended.
Code Examples: Promise.allSettled()
Let's use the same scenario where promise2
rejects, but this time with Promise.allSettled()
.
[
{ "status": "fulfilled", "value": "Value 1"},
{ "status": "rejected", "reason": "Error from Promise 2"},
{ "status": "fulfilled", "value": "Value 3"}
]
Notice how Promise.allSettled()
waited for all promises to complete and gave us a detailed report of each outcome."
Code Examples: Promise.race()
Let's see who wins the race!
Example 1: Fastest Promise Succeeds
P2 (1s) is the fastest and resolves successfully.
Example 2: Fastest Promise Rejects
P3 (2s) is the fastest, but it rejects.
Code Examples: Promise.any()
Let's see how Promise.any()
seeks the first success.
Example 1: First Success Wins (Ignoring Earlier Failure)
P2 rejects quickly (1s), but P3 resolves successfully later (2s). Promise.any()
should give us P3's result.
Example 2: Waiting for the Only Success
P3 fails first (2s), P1 fails next (3s), but P2 eventually succeeds (5s).
Example 3: All Promises Fail
If every promise rejects, Promise.any()
rejects with an AggregateError
.
Promise.any failed as all promises rejected.
Aggregate Error: [AggregateError: All promises were rejected] {
[errors]: [ 'Error P1', 'Error P2', 'Error P3' ]
}
Individual errors: [ 'Error P1', 'Error P2', 'Error P3' ]
Handling Aggregate Errors in Promise.any()
When Promise.any()
rejects because all input promises failed, the error object it provides is an AggregateError
. This object has a useful property: errors
.
aggregateError.errors
: This is an array containing the rejection reason from each of the promises that failed, in the order they were originally passed toPromise.any()
.
Key Terms & Concepts
- Pending: The initial state; the promise is neither fulfilled nor rejected.
- Fulfilled (or Resolved): The asynchronous operation completed successfully. The promise now has a resulting value. This is a final state.
- Rejected: The asynchronous operation failed. The promise now has a reason (usually an error object) for the failure. This is also a final state.
-
Settled: The promise is no longer pending; it has either been fulfilled or rejected. It has reached its final state.
Think of it like this:
- A promise settles when it's done.
- Settling can result in fulfillment (success) or rejection (failure).
resolve()
is the function you call to move a promise to the fulfilled state.reject()
is the function you call to move a promise to the rejected state.