retry-async-lite
v1.0.0
Published
Zero-dependency, lightweight, high-performance async retry utility with configurable attempts, backoff, jitter, timeouts, and cancellation.
Maintainers
Readme
retry-async-lite 🚀
A zero-dependency, ultra-lightweight (~1KB), high-performance JavaScript/TypeScript utility to retry failing asynchronous operations with flexible backoff, jitter, timeouts, and cancellation.
Every network request or external service call can fail transiently. retry-async-lite provides a clean, robust, type-safe API for automatic retry logic in Node.js, browsers, and edge environments.
✨ Features
- 📦 Zero Dependencies: Pure JavaScript, lightweight footprint (~1KB).
- 🔄 Multiple Backoff Strategies:
exponential,linear,static, or custom backoff functions. - 🎲 Jitter Support: Prevent thundering-herd API congestion (
full,equal, or boolean). - ⏱️ Dual Timeouts: Built-in per-attempt timeout and total retry sequence timeout.
- 🛡️ Error Filtering:
retryIfpredicate to skip retries on non-retriable errors (e.g., HTTP 404/401 vs 503). - 🚫 AbortSignal Cancellation: Cancel pending retries instantly using standard Web
AbortSignal. - 🌐 Dual ESM & CommonJS: Works seamlessly with
importandrequire(). - 🔷 TypeScript First: Ship complete, crisp type definitions out of the box.
📦 Installation
npm install retry-async-lite
# or
pnpm add retry-async-lite
# or
yarn add retry-async-lite⚡ Quick Start
Basic Usage
import { retry } from 'retry-async-lite';
// Automatically retry up to 3 times with exponential backoff
const data = await retry(async () => {
const res = await fetch('https://api.example.com/data');
if (!res.ok) throw new Error(`HTTP Error ${res.status}`);
return res.json();
});🛠️ Advanced Recipes
1. HTTP API Retries (Only retry 5xx server errors)
import { retry } from 'retry-async-lite';
const user = await retry(
async ({ attempt }) => {
console.log(`Fetch attempt #${attempt}...`);
const res = await fetch('/api/user/123');
if (!res.ok) {
const err = new Error(`Request failed with status ${res.status}`);
err.status = res.status;
throw err;
}
return res.json();
},
{
attempts: 5,
delay: 500,
backoff: 'exponential',
factor: 2,
jitter: 'full', // Randomize delay to reduce server load spikes
retryIf: (err) => err.status >= 500, // Do NOT retry 4xx client errors
onRetry: ({ error, attempt, nextDelay }) => {
console.warn(`Attempt ${attempt} failed (${error.message}). Retrying in ${nextDelay}ms...`);
},
}
);2. Timeouts & Cancellation
import { retry } from 'retry-async-lite';
const controller = new AbortController();
// Abort after 5 seconds total if user navigates away or cancels
setTimeout(() => controller.abort('User cancelled'), 5000);
try {
const result = await retry(
async () => {
return await performComplexJob();
},
{
attempts: 4,
timeout: 2000, // Timeout each individual attempt at 2 seconds
totalTimeout: 10000, // Timeout the entire operation at 10 seconds total
signal: controller.signal,
}
);
} catch (err) {
if (err.name === 'AbortError') {
console.log('Operation was aborted!');
} else if (err.name === 'TimeoutError') {
console.log('Timed out!');
}
}📖 API Reference
retry(fn, options) / retryAsync(fn, options)
Executes fn(context) with automatic retries according to options.
Parameters
fn:(context: { attempt: number }) => Promise<T>— Async function to run.options:RetryOptions<T>— Configuration object.
Options
| Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| attempts | number | 3 | Total maximum attempts (including initial invocation). |
| delay | number | 1000 | Base delay between retries in milliseconds. |
| maxDelay | number | Infinity | Cap on maximum backoff delay in milliseconds. |
| backoff | 'exponential' \| 'linear' \| 'static' \| Function | 'exponential' | Strategy for calculating backoff delays. |
| factor | number | 2 | Multiplier factor when using exponential backoff. |
| jitter | boolean \| 'full' \| 'equal' | false | Adds randomness to backoff delay to mitigate thundering herd problems. |
| timeout | number | 0 | Timeout per attempt in milliseconds (0 disables timeout). |
| totalTimeout | number | 0 | Overall timeout for the complete retry sequence (0 disables). |
| retryIf | (error, attempt) => boolean \| Promise<boolean> | () => true | Predicate to determine if error should trigger a retry. |
| onRetry | (info) => void \| Promise<void> | undefined | Callback invoked before sleeping and retrying. |
| signal | AbortSignal | undefined | Standard AbortSignal to cancel execution mid-way. |
🚨 Error Classes
retry-async-lite exports three explicit Error classes:
RetryError: Thrown when all attempts are exhausted. Contains.errors(array of errors from each failed attempt),.attempts, and.lastError.TimeoutError: Thrown when an individual attempt or the total sequence times out. Contains.timeoutMs.AbortError: Thrown when execution is cancelled viaAbortSignal.
💻 CommonJS Support
retry-async-lite is dual-packaged for both modern ESM and CommonJS:
// ESM
import { retry, RetryError } from 'retry-async-lite';
// CommonJS
const { retry, RetryError } = require('retry-async-lite');📄 License
MIT © Mukund
