Aller au contenu principal

retry()

retry<T>(fn, options): Promise<T>

Retries an async function with configurable backoff and error filtering.

WHY WRAPPING NATIVE?

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

Interface

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