📚 5 min read
Racing Tasks Examples ​
This page demonstrates practical examples of executing tasks in a racing pattern, where the first task to complete determines the outcome.
Basic Racing Pattern ​
typescript
// Basic racing pattern with timeout
async function raceWithTimeout<T>(
  task: () => Promise<T>,
  timeout: number
): Promise<T> {
  return Promise.race([
    task(),
    new Promise<never>((_, reject) =>
      setTimeout(() => reject(new Error('Task timeout')), timeout)
    ),
  ]);
}
// Usage
try {
  const result = await raceWithTimeout(
    async () => {
      const response = await fetch('/api/data');
      return response.json();
    },
    5000 // 5 second timeout
  );
  console.log('Task completed:', result);
} catch (error) {
  console.error('Task failed or timed out:', error);
}Multiple Data Sources ​
typescript
class RedundantDataFetcher {
  private sources: DataSource[];
  constructor(sources: DataSource[]) {
    this.sources = sources;
  }
  async fetch<T>(path: string, options: FetchOptions = {}): Promise<T> {
    const fetchPromises = this.sources.map(async (source) => {
      try {
        const startTime = Date.now();
        const response = await source.fetch(path);
        const duration = Date.now() - startTime;
        if (!response.ok) {
          throw new Error(`HTTP ${response.status}`);
        }
        return {
          data: await response.json(),
          source: source.name,
          duration,
        };
      } catch (error) {
        console.warn(`Failed to fetch from ${source.name}:`, error);
        throw error;
      }
    });
    try {
      const { data, source, duration } = await Promise.race(fetchPromises);
      console.log(`Data fetched from ${source} in ${duration}ms`);
      return data;
    } catch (error) {
      throw new Error('All data sources failed');
    }
  }
}
// Usage
const fetcher = new RedundantDataFetcher([
  new PrimaryDataSource(),
  new BackupDataSource(),
  new FallbackDataSource(),
]);
const data = await fetcher.fetch('/api/critical-data');Service Discovery ​
typescript
class ServiceDiscovery {
  private registries: ServiceRegistry[];
  private cache: Map<string, ServiceInfo>;
  constructor(registries: ServiceRegistry[]) {
    this.registries = registries;
    this.cache = new Map();
  }
  async discoverService(
    serviceName: string,
    options: DiscoveryOptions = {}
  ): Promise<ServiceInfo> {
    // Check cache first
    const cached = this.cache.get(serviceName);
    if (cached && !this.isExpired(cached)) {
      return cached;
    }
    const discoveryPromises = this.registries.map(async (registry) => {
      const startTime = Date.now();
      try {
        const info = await registry.lookup(serviceName);
        return {
          ...info,
          registry: registry.name,
          latency: Date.now() - startTime,
        };
      } catch (error) {
        console.warn(`Discovery failed for ${registry.name}:`, error);
        throw error;
      }
    });
    try {
      const result = await Promise.race(discoveryPromises);
      this.cache.set(serviceName, {
        ...result,
        timestamp: Date.now(),
      });
      return result;
    } catch (error) {
      throw new Error(`Service ${serviceName} not found in any registry`);
    }
  }
  private isExpired(info: ServiceInfo): boolean {
    return Date.now() - info.timestamp > 60000; // 1 minute TTL
  }
}Real-World Example: Load Balancer ​
typescript
class LoadBalancer {
  private servers: ServerInstance[];
  private healthChecks: Map<string, HealthStatus>;
  private readonly healthCheckInterval: number;
  constructor(servers: ServerInstance[], healthCheckInterval: number = 30000) {
    this.servers = servers;
    this.healthChecks = new Map();
    this.healthCheckInterval = healthCheckInterval;
    this.startHealthChecks();
  }
  async handleRequest<T>(request: Request): Promise<T> {
    const healthyServers = this.getHealthyServers();
    if (healthyServers.length === 0) {
      throw new Error('No healthy servers available');
    }
    const serverPromises = healthyServers.map(async (server) => {
      const startTime = Date.now();
      try {
        const response = await server.handleRequest(request);
        return {
          response,
          server,
          latency: Date.now() - startTime,
        };
      } catch (error) {
        this.markServerUnhealthy(server);
        throw error;
      }
    });
    try {
      const { response, server, latency } = await Promise.race(serverPromises);
      this.updateServerMetrics(server, latency);
      return response;
    } catch (error) {
      throw new Error('All servers failed to handle request');
    }
  }
  private startHealthChecks(): void {
    setInterval(async () => {
      const checks = this.servers.map(async (server) => {
        try {
          const startTime = Date.now();
          await server.healthCheck();
          this.healthChecks.set(server.id, {
            healthy: true,
            lastCheck: Date.now(),
            latency: Date.now() - startTime,
          });
        } catch (error) {
          this.healthChecks.set(server.id, {
            healthy: false,
            lastCheck: Date.now(),
            error: error as Error,
          });
        }
      });
      await Promise.all(checks);
    }, this.healthCheckInterval);
  }
  private getHealthyServers(): ServerInstance[] {
    return this.servers.filter((server) => {
      const health = this.healthChecks.get(server.id);
      return health?.healthy ?? false;
    });
  }
  private markServerUnhealthy(server: ServerInstance): void {
    this.healthChecks.set(server.id, {
      healthy: false,
      lastCheck: Date.now(),
      error: new Error('Request failed'),
    });
  }
  private updateServerMetrics(server: ServerInstance, latency: number): void {
    // Update server metrics for load balancing decisions
    server.metrics.recordLatency(latency);
    server.metrics.incrementRequestCount();
  }
}
// Usage
const loadBalancer = new LoadBalancer([
  new ServerInstance('server1', 'http://server1.example.com'),
  new ServerInstance('server2', 'http://server2.example.com'),
  new ServerInstance('server3', 'http://server3.example.com'),
]);
try {
  const response = await loadBalancer.handleRequest({
    method: 'GET',
    path: '/api/data',
  });
  console.log('Request handled successfully:', response);
} catch (error) {
  console.error('Request failed:', error);
}Best Practices ​
- Timeout handling: typescript- function withTimeout<T>( promise: Promise<T>, timeoutMs: number, errorMessage: string = 'Operation timed out' ): Promise<T> { let timeoutId: NodeJS.Timeout; const timeoutPromise = new Promise<never>((_, reject) => { timeoutId = setTimeout(() => { reject(new Error(errorMessage)); }, timeoutMs); }); return Promise.race([promise, timeoutPromise]).finally(() => { clearTimeout(timeoutId); }); }
- Cancellation support: typescript- class RaceController { private abortController = new AbortController(); async raceWithCancellation<T>(tasks: Array<() => Promise<T>>): Promise<T> { const { signal } = this.abortController; const racingTasks = tasks.map(async (task) => { if (signal.aborted) { throw new Error('Operation cancelled'); } return await task(); }); try { return await Promise.race(racingTasks); } finally { this.cleanup(); } } cancel(): void { this.abortController.abort(); } private cleanup(): void { // Cleanup resources } }
- Error handling: typescript- async function raceWithErrorBoundary<T>( tasks: Array<() => Promise<T>>, options: { timeout?: number; errorHandler?: (error: Error) => void; } = {} ): Promise<T> { const errors: Error[] = []; const wrappedTasks = tasks.map(async (task) => { try { return await task(); } catch (error) { errors.push(error as Error); options.errorHandler?.(error as Error); throw error; } }); if (options.timeout) { wrappedTasks.push( new Promise<never>((_, reject) => setTimeout(() => reject(new Error('Timeout')), options.timeout) ) ); } try { return await Promise.race(wrappedTasks); } catch (error) { if (errors.length === tasks.length) { throw new AggregateError(errors, 'All tasks failed'); } throw error; } }
- Resource cleanup: typescript- class ResourceManager { private resources: Set<Resource> = new Set(); async raceWithResources<T>( operations: Array<(resource: Resource) => Promise<T>> ): Promise<T> { const acquiredResources: Resource[] = []; try { // Acquire resources for (const resource of this.resources) { await resource.acquire(); acquiredResources.push(resource); } // Race operations return await Promise.race( operations.map((op) => this.executeWithResource(op, acquiredResources[0]) ) ); } finally { // Release resources for (const resource of acquiredResources) { try { await resource.release(); } catch (error) { console.error('Resource cleanup failed:', error); } } } } private async executeWithResource<T>( operation: (resource: Resource) => Promise<T>, resource: Resource ): Promise<T> { try { return await operation(resource); } catch (error) { console.error('Operation failed:', error); throw error; } } }