One source fails early, but the application still wants usable data from another. Promise.any fits a requirement for one fulfillment. Define Promise success separately from data validity, and decide how to report the absence of a usable source.
Run each block inside an async function or with top-level await. All data stays in memory, without network calls or timers.
1. any does not select the first rejection like race
const jobs = [
Promise.reject(new Error('source A failed')),
Promise.resolve('source B data')
];
try {
await Promise.race(jobs);
} catch (error) {
console.log(error.message); // source A failed
}
console.log(await Promise.any(jobs)); // source B data
race follows the first settlement; any waits for a fulfillment. See MDN Promise.race and MDN Promise.any. These already-settled inputs demonstrate ordering, not network speed.
Use interchangeable candidates under an explicit contract. A screen requiring both a profile and its permissions cannot satisfy that requirement with just one arbitrary result. Specify the minimum required data before choosing a combinator.
2. Inspect the collected reasons when every candidate rejects
let failA;
let failB;
const a = new Promise((_, reject) => { failA = reject; });
const b = new Promise((_, reject) => { failB = reject; });
const selected = Promise.any([a, b]);
failB(new Error('B unavailable'));
failA(new Error('A unavailable'));
try {
await selected;
} catch (error) {
console.log(error instanceof AggregateError); // true
console.log(error.errors.map(item => item.message).join('|'));
// A unavailable|B unavailable
}
AggregateError collects rejection reasons in errors; see MDN AggregateError. Although B rejects first here, errors corresponds to input order A then B.
Keep source identifiers alongside input positions. The first collected reason is not necessarily the earliest failed dependency. Real rejection reasons need not be Error objects; reading message here relies on the Error instances created by this example.
The UI can use a short failure message while diagnostics retain the source association needed to investigate each dependency.
3. Empty input reaches the error path
try {
await Promise.any([]);
} catch (error) {
console.log(error instanceof AggregateError); // true
console.log(error.errors.length); // 0
}
No candidates do not produce a default successful value. This example yields AggregateError with no collected reasons. Distinguish an unconfigured source list from sources that were attempted and all failed.
Also define a waiting policy: one rejected input and another forever-pending input do not establish that every candidate failed. Manage deadlines and cleanup where the application owns the operations.
4. false and invalid payloads can still be fulfillments
const flag = await Promise.any([
Promise.resolve(false),
Promise.resolve(true)
]);
const payload = await Promise.any([
Promise.resolve({ ok: false, data: null }),
Promise.resolve({ ok: true, data: 'ready' })
]);
console.log(flag); // false
console.log(payload.ok); // false
The false value is a normal fulfillment, and ok:false does not automatically reject its Promise. Validate each candidate against the required schema and business conditions before selection. Validation after selection does not make any resume searching for another result.
Selecting a result does not cancel remaining work. Account for the load of multiple sources and resource cleanup; any is not a retry policy or concurrency limit.
5. Review fallback selection
- Do candidates provide equivalent replacement data?
- Does each task validate the required result contract?
- Is empty configuration handled explicitly?
- Are errors associated with sources in input order?
- Who owns deadlines and unselected work?
All eight bilingual blocks ran with Node.js, with additional early-rejection, non-Error reason and remaining-work checks. Real network sources and performance were not tested.




No comments yet. Be the first to share your thoughts.