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]);
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