Skip to main content
Races a promise against a timer. Rejects with TimeoutError if the promise does not resolve within the specified milliseconds. If a fallback value is provided, returns it instead of throwing on timeout.

Signature

function timeout<T>(
  promise: PromiseLike<T> | (() => PromiseLike<T> | T),
  ms: number,
  options?: StandardOptions & { fallback?: T },
): Promise<T>

Parameters

promise
PromiseLike<T> | (() => PromiseLike<T> | T)
required
The promise to race against the timer, or a function that returns a promise.
ms
number
required
The timeout duration in milliseconds.
options
StandardOptions & { fallback?: T }
Timeout options.

Returns

A promise that resolves with the result of the promise if it completes in time, or the fallback value if provided.

Throws

  • TimeoutError if the promise does not resolve within the timeout and no fallback is provided
  • AbortError if the signal is aborted
  • Any error thrown by the promise

Examples

Basic timeout

import { timeout } from '@temelj/async';

try {
  const result = await timeout(fetchData(), 5000);
  console.log(result);
} catch (error) {
  if (error instanceof TimeoutError) {
    console.error('Request timed out after 5 seconds');
  }
}

With fallback value

import { timeout } from '@temelj/async';

// Returns 'default' if fetch takes longer than 3 seconds
const result = await timeout(
  fetch('/api/data').then(r => r.json()),
  3000,
  { fallback: 'default' }
);

console.log(result); // Either fetched data or 'default'

With factory function

import { timeout } from '@temelj/async';

// Function is only called when timeout starts
const result = await timeout(
  () => expensiveOperation(),
  2000
);

API request timeout

import { timeout } from '@temelj/async';

async function fetchWithTimeout(url: string) {
  try {
    const response = await timeout(
      fetch(url),
      5000 // 5 second timeout
    );
    return await response.json();
  } catch (error) {
    if (error instanceof TimeoutError) {
      throw new Error(`Request to ${url} timed out`);
    }
    throw error;
  }
}

Database query timeout

import { timeout } from '@temelj/async';

const result = await timeout(
  db.query('SELECT * FROM large_table'),
  10000, // 10 second timeout
  { fallback: [] } // Return empty array on timeout
);

Multiple operations with different timeouts

import { timeout } from '@temelj/async';

const [users, posts, comments] = await Promise.all([
  timeout(fetchUsers(), 5000, { fallback: [] }),
  timeout(fetchPosts(), 3000, { fallback: [] }),
  timeout(fetchComments(), 2000, { fallback: [] }),
]);

Retry with timeout

import { timeout, retry } from '@temelj/async';

const result = await retry(
  async (attempt) => {
    return await timeout(
      fetch('/api/data'),
      2000 // Each attempt has 2 second timeout
    );
  },
  { times: 3 }
);

Progressive timeout

import { timeout } from '@temelj/async';

async function fetchWithProgressiveTimeout(url: string) {
  // Try with short timeout first
  try {
    return await timeout(fetch(url), 1000);
  } catch (error) {
    if (error instanceof TimeoutError) {
      // Retry with longer timeout
      console.log('Retrying with longer timeout...');
      return await timeout(fetch(url), 5000);
    }
    throw error;
  }
}

With abort signal

import { timeout } from '@temelj/async';

const controller = new AbortController();

const task = timeout(
  fetch('/api/data', { signal: controller.signal }),
  10000,
  { signal: controller.signal }
);

// Cancel from user action
button.addEventListener('click', () => {
  controller.abort();
});

try {
  await task;
} catch (error) {
  if (error instanceof AbortError) {
    console.log('Request cancelled by user');
  } else if (error instanceof TimeoutError) {
    console.log('Request timed out');
  }
}

Cache with timeout

import { timeout } from '@temelj/async';

class CachedAPI {
  private cache = new Map<string, any>();
  
  async fetch(key: string, fetcher: () => Promise<any>) {
    const cached = this.cache.get(key);
    if (cached) return cached;
    
    const result = await timeout(
      fetcher(),
      5000,
      { fallback: null }
    );
    
    if (result !== null) {
      this.cache.set(key, result);
    }
    
    return result;
  }
}

Race with timeout

import { timeout } from '@temelj/async';

// Fetch from multiple sources, use first to respond within timeout
const sources = [
  'https://api1.example.com/data',
  'https://api2.example.com/data',
  'https://api3.example.com/data',
];

const result = await Promise.race(
  sources.map(url => 
    timeout(
      fetch(url).then(r => r.json()),
      3000,
      { fallback: null }
    )
  )
);

Graceful degradation

import { timeout } from '@temelj/async';

async function loadDashboard() {
  const [userData, analytics, notifications] = await Promise.all([
    // Critical data - no fallback
    timeout(fetchUserData(), 5000),
    
    // Optional data - use fallback
    timeout(fetchAnalytics(), 3000, { 
      fallback: { views: 0, clicks: 0 } 
    }),
    
    // Optional data - use fallback
    timeout(fetchNotifications(), 2000, { 
      fallback: [] 
    }),
  ]);
  
  return { userData, analytics, notifications };
}

Build docs developers (and LLMs) love