Difficulty: Intermediate
Explain Promises in depth. How do Promise.all, Promise.race, Promise.allSettled, and Promise.any differ?
A Promise represents an asynchronous operation that will eventually resolve with a value or reject with a reason. Promises have three states: pending, fulfilled, and rejected. Once settled (fulfilled or rejected), a promise's state cannot change.
The .then() method handles fulfilled values, .catch() handles rejections, and .finally() runs regardless of outcome. Promises chain naturally - each .then() returns a new promise.
Promise combinators handle multiple promises: - Promise.all(): Resolves when ALL promises fulfill; rejects if ANY one rejects. - Promise.allSettled(): Waits for ALL to settle; never rejects. Returns status and value/reason for each. - Promise.race(): Settles as soon as the FIRST promise settles (fulfill or reject). - Promise.any(): Resolves with the FIRST fulfilled promise; rejects only if ALL reject (AggregateError).
// Creating a promise
function fetchUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) resolve({ id, name: `User ${id}` });
else reject(new Error('Invalid ID'));
}, 100);
});
}
// Chaining
fetchUser(1)
.then(user => {
console.log(user);
return fetchUser(2); // return another promise
})
.then(user => console.log(user))
.catch(err => console.log(err.message))
.finally(() => console.log('Done'));
// Error propagation
fetchUser(-1)
.then(user => console.log(user)) // skipped
.catch(err => console.log('Error:', err.message));
Returning a promise from .then() chains them sequentially. Errors propagate down the chain to the nearest .catch(). finally runs regardless.
const fast = new Promise(res => setTimeout(() => res('fast'), 100));
const slow = new Promise(res => setTimeout(() => res('slow'), 200));
const fail = new Promise((_, rej) => setTimeout(() => rej('error'), 150));
// Promise.all - fails fast on first rejection
Promise.all([fast, slow])
.then(results => console.log('all:', results));
Promise.all([fast, fail, slow])
.catch(err => console.log('all error:', err));
// Promise.allSettled - never rejects, reports all results
Promise.allSettled([fast, fail, slow])
.then(results => {
results.forEach(r => {
if (r.status === 'fulfilled') console.log('OK:', r.value);
else console.log('FAIL:', r.reason);
});
});
Promise.all short-circuits on first rejection. Promise.allSettled always waits for all and returns {status, value/reason} for each.
const p1 = new Promise(res => setTimeout(() => res('p1'), 300));
const p2 = new Promise(res => setTimeout(() => res('p2'), 100));
const p3 = new Promise((_, rej) => setTimeout(() => rej('p3 failed'), 50));
// race - first to settle (fulfill OR reject)
Promise.race([p1, p2, p3])
.then(val => console.log('race won:', val))
.catch(err => console.log('race error:', err));
// any - first to FULFILL (ignores rejections)
Promise.any([p3, p2, p1])
.then(val => console.log('any won:', val));
// any - all reject = AggregateError
const f1 = Promise.reject('err1');
const f2 = Promise.reject('err2');
Promise.any([f1, f2])
.catch(err => console.log('any all failed:', err.errors));
race settles with whatever finishes first (even rejections). any only cares about fulfillments and rejects only when all promises reject.
Promise, then, catch, finally, Promise.all, Promise.allSettled, Promise.race