typescriptintermediate

Promise Concurrency Patterns

Master Promise.all, allSettled, race, any — parallel execution with error handling and timeouts.

typescript
// Simulate async operations
const fetchUser = (id: number) =>
  new Promise<string>((resolve, reject) =>
    setTimeout(() => {
      if (id > 0) resolve(`User-${id}`);
      else reject(new Error(`Invalid id: ${id}`));
    }, Math.random() * 200)
  );

// Promise.all — fail fast (all or nothing)
async function fetchAllUsers(ids: number[]) {
  try {
    const users = await Promise.all(ids.map(fetchUser));
    console.log('All users:', users);
  } catch (e) {
    console.error('One failed, all rejected:', (e as Error).message);
  }
}

// Promise.allSettled — never throws, get all results
async function fetchUsersSettled(ids: number[]) {
  const results = await Promise.allSettled(ids.map(fetchUser));

  const succeeded = results
    .filter((r): r is PromiseFulfilledResult<string> => r.status === 'fulfilled')
    .map((r) => r.value);

  const failed = results
    .filter((r): r is PromiseRejectedResult => r.status === 'rejected')
    .map((r) => r.reason.message);

  console.log('Succeeded:', succeeded);
  console.log('Failed:', failed);
}

// Promise.race — first to settle wins
async function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
  const timeout = new Promise<never>((_, reject) =>
    setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms)
  );
  return Promise.race([promise, timeout]);
}

// Promise.any — first to succeed wins (ignores rejections)
async function fetchFromFastestMirror() {
  const mirrors = [
    fetchUser(1), // fast
    fetchUser(2), // medium
    fetchUser(3), // slow
  ];
  try {
    const fastest = await Promise.any(mirrors);
    console.log('Fastest:', fastest);
  } catch (e) {
    console.error('All failed:', (e as AggregateError).errors);
  }
}

// Concurrency limiter
async function pMap<T, R>(
  items: T[],
  fn: (item: T) => Promise<R>,
  concurrency: number
): Promise<R[]> {
  const results: R[] = [];
  let index = 0;

  async function worker() {
    while (index < items.length) {
      const i = index++;
      results[i] = await fn(items[i]);
    }
  }

  const workers = Array.from(
    { length: Math.min(concurrency, items.length) },
    () => worker()
  );
  await Promise.all(workers);
  return results;
}

// Usage
await fetchAllUsers([1, 2, 3]);
await fetchUsersSettled([1, -1, 3]);
await fetchFromFastestMirror();

// Limited concurrency: 3 at a time
const ids = Array.from({ length: 10 }, (_, i) => i + 1);
const results = await pMap(ids, fetchUser, 3);
console.log('pMap results:', results);

// Timeout
try {
  await withTimeout(fetchUser(1), 50);
} catch (e) {
  console.log('Timed out:', (e as Error).message);
}

Use Cases

  • Parallel API calls with error handling
  • Timeout and race conditions
  • Controlled concurrency

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.