Skip to content
📚 3 min read

Racing Task Execution Examples ​

Learn how to implement racing patterns for competitive task execution.

Basic Usage ​

typescript
// Simple racing with timeout
async function executeWithTimeout<T>(
  task: () => Promise<T>,
  timeoutMs: number
): Promise<T> {
  const timeoutPromise = new Promise<never>((_, reject) =>
    setTimeout(() => reject(new Error('Task timed out')), timeoutMs)
  );

  return Promise.race([task(), timeoutPromise]);
}

// First successful result
async function executeUntilSuccess<T>(tasks: (() => Promise<T>)[]): Promise<T> {
  return Promise.any(tasks.map((task) => task()));
}

Advanced Patterns ​

Race with Cleanup ​

typescript
interface RaceResult<T> {
  result: T;
  winner: number;
  cleanup: () => Promise<void>;
}

async function raceWithCleanup<T>(
  tasks: (() => Promise<T>)[]
): Promise<RaceResult<T>> {
  const cleanups: (() => Promise<void>)[] = [];

  const racingTasks = tasks.map(async (task, index) => {
    try {
      const result = await task();
      return {
        result,
        winner: index,
        cleanup: async () => {
          await Promise.all(cleanups.map((cleanup) => cleanup()));
        },
      };
    } catch (error) {
      if (cleanups[index]) {
        await cleanups[index]();
      }
      throw error;
    }
  });

  return Promise.race(racingTasks);
}

// Usage example
const result = await raceWithCleanup([
  async () => {
    const controller = new AbortController();
    cleanups[0] = async () => controller.abort();
    return fetch(url1, { signal: controller.signal });
  },
  async () => {
    const controller = new AbortController();
    cleanups[1] = async () => controller.abort();
    return fetch(url2, { signal: controller.signal });
  },
]);

Progressive Racing ​

typescript
interface ProgressiveRaceOptions<T> {
  tasks: (() => Promise<T>)[];
  validateResult: (result: T) => boolean;
  timeout: number;
  maxAttempts: number;
}

async function progressiveRace<T>({
  tasks,
  validateResult,
  timeout,
  maxAttempts,
}: ProgressiveRaceOptions<T>): Promise<T> {
  const attempts = new Set<number>();
  let currentTimeout = timeout;

  while (attempts.size < Math.min(tasks.length, maxAttempts)) {
    // Select next task that hasn't been attempted
    const availableTasks = tasks
      .map((task, index) => ({ task, index }))
      .filter(({ index }) => !attempts.has(index));

    if (availableTasks.length === 0) break;

    const { task, index } = availableTasks[0];
    attempts.add(index);

    try {
      const result = await executeWithTimeout(task, currentTimeout);
      if (validateResult(result)) {
        return result;
      }
    } catch (error) {
      console.warn(`Task ${index} failed:`, error);
    }

    // Increase timeout for next attempt
    currentTimeout *= 1.5;
  }

  throw new Error('All racing attempts failed');
}

Competitive Racing ​

typescript
interface Competitor<T> {
  execute: () => Promise<T>;
  priority: number;
  timeout: number;
}

async function competitiveRace<T>(competitors: Competitor<T>[]): Promise<T> {
  // Sort by priority (higher priority starts first)
  const sorted = [...competitors].sort((a, b) => b.priority - a.priority);

  const results = new Map<number, Promise<T>>();
  const timeouts = new Map<number, NodeJS.Timeout>();

  for (let i = 0; i < sorted.length; i++) {
    const competitor = sorted[i];

    // Start execution after delay based on priority
    const startDelay = i * 100; // 100ms between each competitor

    const competitorPromise = new Promise<T>((resolve, reject) => {
      const timeout = setTimeout(async () => {
        try {
          const result = await competitor.execute();
          resolve(result);
        } catch (error) {
          reject(error);
        }
      }, startDelay);

      timeouts.set(i, timeout);
    });

    results.set(i, competitorPromise);

    // Set competitor timeout
    const timeoutPromise = new Promise<T>((_, reject) => {
      setTimeout(() => {
        reject(new Error(`Competitor ${i} timed out`));
      }, competitor.timeout + startDelay);
    });

    // Race between completion and timeout
    results.set(i, Promise.race([competitorPromise, timeoutPromise]));
  }

  try {
    // Wait for first successful result
    const result = await Promise.any(Array.from(results.values()));

    // Cleanup remaining timeouts
    timeouts.forEach((timeout) => clearTimeout(timeout));

    return result;
  } catch (error) {
    timeouts.forEach((timeout) => clearTimeout(timeout));
    throw error;
  }
}

// Usage example
const result = await competitiveRace([
  {
    priority: 3,
    timeout: 5000,
    execute: async () => fetch('primary-endpoint'),
  },
  {
    priority: 2,
    timeout: 3000,
    execute: async () => fetch('secondary-endpoint'),
  },
  {
    priority: 1,
    timeout: 2000,
    execute: async () => fetch('fallback-endpoint'),
  },
]);