Skip to content
📚 2 min read

Promise.race Examples ​

Learn how to use Promise.race for implementing timeouts and racing conditions.

Basic Usage ​

typescript
// Basic timeout pattern
async function fetchWithTimeout(url: string, timeoutMs: number) {
  return Promise.race([
    fetch(url),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error('Request timed out')), timeoutMs)
    ),
  ]);
}

// First response wins pattern
async function fetchFromMultipleEndpoints(urls: string[]) {
  return Promise.race(urls.map((url) => fetch(url)));
}

Advanced Patterns ​

Timeout with Cleanup ​

typescript
async function fetchWithTimeoutAndCleanup(url: string, timeoutMs: number) {
  const controller = new AbortController();
  const { signal } = controller;

  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => {
      controller.abort();
      reject(new Error('Request timed out'));
    }, timeoutMs);
  });

  return Promise.race([fetch(url, { signal }), timeoutPromise]);
}

Race with Fallback ​

typescript
async function fetchWithFallback<T>(
  primaryFetch: () => Promise<T>,
  fallbackFetch: () => Promise<T>,
  timeoutMs: number
): Promise<T> {
  let fallbackTimer: NodeJS.Timeout;

  const primary = primaryFetch().catch((error) => {
    console.warn('Primary fetch failed:', error);
    throw error;
  });

  const fallback = new Promise<T>((resolve, reject) => {
    fallbackTimer = setTimeout(async () => {
      try {
        const result = await fallbackFetch();
        resolve(result);
      } catch (error) {
        reject(error);
      }
    }, timeoutMs);
  });

  try {
    const result = await Promise.race([primary, fallback]);
    clearTimeout(fallbackTimer);
    return result;
  } catch (error) {
    clearTimeout(fallbackTimer);
    throw error;
  }
}

Race with Progress Updates ​

typescript
interface ProgressUpdate {
  type: 'progress';
  percent: number;
}

interface Result<T> {
  type: 'result';
  value: T;
}

type ProgressOrResult<T> = ProgressUpdate | Result<T>;

async function* fetchWithProgress<T>(
  fetcher: () => Promise<T>,
  progressInterval: number = 100
): AsyncGenerator<ProgressOrResult<T>, void, unknown> {
  let progress = 0;
  const progressPromise = (async function* () {
    while (progress < 100) {
      progress += Math.random() * 20;
      progress = Math.min(progress, 99);
      yield { type: 'progress', percent: progress };
      await new Promise((resolve) => setTimeout(resolve, progressInterval));
    }
  })();

  const resultPromise = (async function* () {
    const result = await fetcher();
    yield { type: 'result', value: result };
  })();

  const combined = async function* () {
    yield* progressPromise;
    yield* resultPromise;
  };

  for await (const update of combined()) {
    if (update.type === 'result') {
      progress = 100;
    }
    yield update;
  }
}