retry()
retry<
T>(fn,options?):Promise<T>
Retries an async function with configurable backoff and error filtering.
We prefer to wrap this to improve resilience against transient failures, adhering to Fail Fast principles by managing retries declaratively. See Design Philosophy
Type Parametersβ
T: Tβ
The return type of the function.
Parametersβ
fn: () => Promise<T>β
The async function to retry.
options?: RetryOptions = {}β
Retry configuration options.
Returns: Promise<T>β
Promise that resolves to the function's return value.
Throwsβ
The last error thrown by fn if all retries fail.
Sinceβ
1.1.0
Performanceβ
Exponential backoff with maxDelay cap prevents excessive wait times.
Jitter prevents thundering herd problem in distributed systems.
Also known asβ
retry (Radashi, Effect, Modern Dash) Β· β (Lodash, es-toolkit, Remeda, Ramda, Antfu)
Exampleβ
const result = await retry(() => fetchData(), { attempts: 3 });
const result = await retry(() => apiCall(), {
attempts: 5,
delay: 1000,
backoff: 2,
jitter: 0.5,
until: (error) => error.code !== 'PERMANENT_ERROR'
});
How it works?β
Retries a failed async function with configurable backoff and jitter. Exponential backoff increases delay between attempts β jitter adds randomness to prevent thundering herd.
Exponential Backoffβ
Jitter Visualizationβ
Jitter adds randomness to delay to avoid synchronized retries:
Until (Error Filter)β
The until option aborts retry if error is permanent:
Use Casesβ
Handle network failures gracefully πβ
Implement robust retry logic for network operations with exponential backoff. Essential for handling transient network issues and improving reliability.
// Retry API calls with exponential backoff
const userData = await retry(
async () => {
const response = await fetch("/api/users/123");
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
},
{
attempts: 5,
delay: 1000,
backoff: 2,
maxDelay: 10000,
until: (error) => error.message.includes("HTTP 5"),
}
);
console.log("User data loaded:", userData);
Recover from temporary service unavailabilityβ
Handle temporary service outages with intelligent retry mechanisms. Critical for maintaining service availability and user experience.
// Retry database operations
const result = await retry(
async () => {
return await database.query("SELECT * FROM users WHERE active = ?", [true]);
},
{
attempts: 3,
delay: 500,
backoff: 1.5,
until: (error) => error.code === "CONNECTION_TIMEOUT",
}
);
console.log(`Found ${result.length} active users`);
Implement file upload resilienceβ
Retry file uploads with progressive delays for better success rates. Essential for handling large file uploads and network instability.
// Retry file upload with custom error handling
const uploadResult = await retry(
async () => {
const formData = new FormData();
formData.append("file", file);
const response = await fetch("/api/upload", {
method: "POST",
body: formData,
});
if (!response.ok) {
throw new Error(`Upload failed: ${response.status}`);
}
return response.json();
},
{
attempts: 4,
delay: 2000,
backoff: 2,
until: (error) => error.message.includes("Upload failed"),
}
);
console.log("File uploaded successfully:", uploadResult);
Manage rate-limited API callsβ
Handle API rate limiting with intelligent retry strategies. Critical for working with external APIs that have rate limits.
// Retry with rate limit handling
const apiData = await retry(
async () => {
const response = await fetch("/external-api/data");
if (response.status === 429) {
throw new Error("RATE_LIMITED");
}
return response.json();
},
{
attempts: 3,
delay: 5000, // Wait 5 seconds for rate limit reset
backoff: 1,
until: (error) => error.message === "RATE_LIMITED",
}
);
console.log("API data retrieved:", apiData);
Ensure critical operations succeedβ
Guarantee execution of critical operations with comprehensive retry logic. Essential for payment processing, data synchronization, and critical business operations.
// Retry critical payment processing
const paymentResult = await retry(
async () => {
return await processPayment({
amount: 100.0,
currency: "USD",
paymentMethod: "card",
});
},
{
attempts: 5,
delay: 1000,
backoff: 2,
maxDelay: 30000,
until: (error) => error.code !== "INVALID_CARD",
}
);
console.log("Payment processed:", paymentResult);
Multi-gateway payment failover for financial systemsβ
Retry payment processing across multiple gateways for maximum success rate. Critical for fintech applications and e-commerce platforms handling payments.
const gateways = ["stripe", "paypal", "adyen"];
let currentGatewayIndex = 0;
const processPaymentWithFailover = await retry(
async () => {
const gateway = gateways[currentGatewayIndex];
const result = await paymentAPI.process({
gateway,
amount: 150.00,
currency: "EUR",
cardToken: encryptedToken,
});
if (!result.success && result.error === "GATEWAY_UNAVAILABLE") {
currentGatewayIndex = (currentGatewayIndex + 1) % gateways.length;
throw new Error("GATEWAY_DOWN");
}
return result;
},
{
attempts: gateways.length * 2,
delay: 500,
backoff: 1.5,
until: (error) => error.message === "GATEWAY_DOWN",
}
);
console.log("Payment completed via:", gateways[currentGatewayIndex]);
Sync offline data when connection returnsβ
Retry syncing queued offline actions when the network comes back. Essential for PWAs and mobile apps with offline-first architecture.
const syncOfflineQueue = async (queue: OfflineAction[]) => {
for (const action of queue) {
await retry(
async () => {
await api.sync(action);
removeFromQueue(action.id);
},
{
attempts: 3,
delay: 2000,
backoff: 2,
until: (error) => (error as Error).message.includes("NETWORK_OFFLINE"),
}
);
}
};
window.addEventListener("online", () => syncOfflineQueue(getOfflineQueue()));
RetryOptionsβ
Configuration options for retry behavior.
Sinceβ
2.0.0
Propertiesβ
attempts?: numberβ
Number of retry attempts. Defaults to 3.
delay?: numberβ
Initial delay between retries in milliseconds. Defaults to 1000.
backoff?: numberβ
Backoff multiplier for delay. Defaults to 1 (no backoff).
maxDelay?: numberβ
Maximum delay between retries in milliseconds. Defaults to 10000.
jitter?: numberβ
Random jitter factor (0-1) to add variation to delay. Defaults to 0.
until()?: (error) => booleanβ
Function to determine if error should trigger retry. Return false to abort.
error:
unknown
Returns:boolean