Reliable timeout wrapper for async operations with AbortController support
npm install @philiprehberger/safe-timeout
Reliable timeout wrapper for async operations with AbortController support
npm install @philiprehberger/safe-timeout
import { withTimeout, TimeoutError } from '@philiprehberger/safe-timeout';
try {
const data = await withTimeout(fetch('/api/data'), 5000);
} catch (error) {
if (error instanceof TimeoutError) {
console.log('Request timed out');
}
}
import { withTimeoutFallback } from '@philiprehberger/safe-timeout';
const data = await withTimeoutFallback(fetch('/api/data'), 5000, cachedData);
// Returns cachedData if the fetch takes longer than 5 seconds
import { createTimeoutSignal } from '@philiprehberger/safe-timeout';
const signal = createTimeoutSignal(5000);
const response = await fetch('/api/data', { signal });
const data = await withTimeout(fetch('/api'), 5000, {
signal: existingAbortSignal, // Also abort on external signal
onTimeout: () => console.warn('Operation timed out'),
});
import { withTimeoutAbort, TimeoutError } from '@philiprehberger/safe-timeout';
try {
const { result, controller } = await withTimeoutAbort(
(signal) => fetch('/api/data', { signal }),
5000,
);
// controller.signal can be passed to follow-up work that should
// be cancelled if the caller decides to bail out later.
} catch (error) {
if (error instanceof TimeoutError) {
// The inner fetch was aborted automatically — no cleanup needed.
console.warn('Request timed out and was aborted');
}
}
import { withTimeoutRetry } from '@philiprehberger/safe-timeout';
const data = await withTimeoutRetry(
(signal) => fetch('/api/data', { signal }),
{
timeoutMs: 2000,
maxRetries: 3,
backoffMultiplier: 2, // 2s -> 4s -> 8s -> 16s
onTimeout: (attempt, ms) => {
console.warn(`Attempt ${attempt} timed out after ${ms}ms`);
},
},
);
import { withDeadline } from '@philiprehberger/safe-timeout';
// Must complete by an absolute time
const deadline = new Date('2026-04-01T00:00:00Z');
const data = await withDeadline(fetch('/api/data'), deadline);
import { TimeoutTracker } from '@philiprehberger/safe-timeout';
const tracker = new TimeoutTracker();
await tracker.run(fetch('/api/users'), 3000);
await tracker.run(fetch('/api/posts'), 3000);
const stats = tracker.stats;
console.log(`Success rate: ${(stats.successRate * 100).toFixed(1)}%`);
console.log(`Avg duration: ${stats.averageDurationMs}ms`);
console.log(`Timeouts: ${stats.timeouts}/${stats.totalCalls}`);
// Reset when needed
tracker.reset();
| Export | Description |
|---|---|
withTimeout(promise, ms, options?) | Race promise against timeout, throws TimeoutError on timeout |
withTimeoutAbort(fn, ms, options?) | Like withTimeout but passes an AbortSignal to fn and aborts it on timeout; resolves with { result, controller } |
withTimeoutFallback(promise, ms, fallback) | Returns fallback value on timeout instead of throwing |
withTimeoutRetry(fn, options) | Retry an async function with increasing timeout on each attempt |
withDeadline(promise, deadline, options?) | Race promise against an absolute Date deadline |
createTimeoutSignal(ms) | Creates an AbortSignal that aborts after ms |
TimeoutTracker | Class that tracks timeout frequency, success rate, and average duration |
TimeoutError | Error class thrown on timeout, has ms property |
TimeoutOptions| Option | Type | Description |
|---|---|---|
signal | AbortSignal | External abort signal |
onTimeout | () => void | Callback when timeout fires |
TimeoutRetryOptions| Option | Type | Default | Description |
|---|---|---|---|
timeoutMs | number | (required) | Initial timeout in milliseconds |
maxRetries | number | 3 | Maximum number of retries after timeout |
backoffMultiplier | number | 2 | Multiplier applied to timeout on each retry |
signal | AbortSignal | — | External abort signal |
onTimeout | (attempt, ms) => void | — | Callback on each timeout before retrying |
TimeoutStats| Property | Type | Description |
|---|---|---|
totalCalls | number | Total number of tracked calls |
successes | number | Calls that completed within timeout |
timeouts | number | Calls that timed out |
errors | number | Calls that failed with non-timeout errors |
successRate | number | Success ratio (0 to 1) |
timeoutRate | number | Timeout ratio (0 to 1) |
averageDurationMs | number | Average duration of successful calls in ms |
npm install
npm run build
npm test
If you find this project useful: