a-smart-retry
v1.0.0
Published
A zero-dependency utility to retry failed async operations with exponential backoff, jitter, timeout, abort support, and circuit breaker pattern.
Maintainers
Readme
smart-retry
A zero-dependency utility to retry failed async operations with exponential backoff, jitter, per-attempt timeout, abort signal, and a built-in circuit breaker pattern. Works with any Promise-based function — API calls, database queries, file operations, etc.
Features
- 🚀 Zero dependencies — lightweight and fast.
- 📈 Exponential Backoff — delays double after each failure (1s → 2s → 4s → 8s…).
- 🎲 Jitter — adds randomization to prevent thundering herd problems.
- ⏱️ Per-Attempt Timeout — kills slow attempts that hang.
- 🛑 Abort Signal — cancel retries externally via
AbortController. - ⚡ Circuit Breaker — automatically stops retrying when a service is clearly down.
- 🎯 Conditional Retry —
shouldRetry(error, attempt)to skip retries for non-retriable errors (e.g., 400 Bad Request). - 🪝 onRetry Hook — log or track each retry attempt.
- 🏭 Factory Pattern —
createRetry()to build pre-configured retry functions.
Installation
npm install a-smart-retryQuick Start
const { retry } = require('smart-retry');
// Retry a flaky API call up to 3 times
const data = await retry(() => fetch('https://api.example.com/data'), {
retries: 3,
baseDelay: 1000
});Advanced Usage
Full Options
const data = await retry(fetchData, {
retries: 5,
baseDelay: 500,
maxDelay: 10000,
jitter: true,
timeout: 5000,
shouldRetry: (err, attempt) => err.status !== 400,
onRetry: (err, attempt, delay) => {
console.log(`Retry ${attempt} in ${delay}ms: ${err.message}`);
}
});Abort Signal
const controller = new AbortController();
setTimeout(() => controller.abort(), 10000); // Cancel after 10s
await retry(fetchData, {
retries: 10,
signal: controller.signal
});Circuit Breaker
const { retry, CircuitBreaker } = require('smart-retry');
const breaker = new CircuitBreaker({
threshold: 5, // Open after 5 consecutive failures
resetTimeout: 30000 // Try again after 30s
});
await retry(callExternalService, {
retries: 3,
circuitBreaker: breaker
});
console.log(breaker.state); // 'CLOSED', 'OPEN', or 'HALF_OPEN'Pre-configured Factory
const { createRetry } = require('smart-retry');
const apiRetry = createRetry({
retries: 3,
baseDelay: 500,
jitter: true,
timeout: 5000
});
// Use everywhere with consistent config
const users = await apiRetry(() => fetchUsers());
const orders = await apiRetry(() => fetchOrders());Options
| Option | Type | Default | Description |
|---|---|---|---|
| retries | number | 3 | Maximum number of retry attempts. |
| baseDelay | number | 1000 | Base delay in ms between retries. |
| maxDelay | number | 30000 | Maximum delay cap in ms. |
| jitter | boolean | true | Add random jitter to delays. |
| timeout | number | 0 | Per-attempt timeout in ms. 0 = no timeout. |
| shouldRetry | Function | () => true | (error, attempt) => boolean |
| onRetry | Function | null | (error, attempt, delay) => void |
| signal | AbortSignal | null | AbortController signal to cancel retries. |
| circuitBreaker | CircuitBreaker | null | Circuit breaker instance. |
License
MIT
