Promises Deep Dive

Difficulty: Intermediate

Question

Explain Promises in depth. How do Promise.all, Promise.race, Promise.allSettled, and Promise.any differ?

Answer

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).

Code examples

Promise Basics and Chaining

// 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.

Promise.all vs Promise.allSettled

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.

Promise.race vs Promise.any

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.

Key points

Concepts covered

Promise, then, catch, finally, Promise.all, Promise.allSettled, Promise.race