retry
Retry async operations until success or maximum retry attempts are reached.
Basic Usage
typescript
import { retry } from 'radash'
const result = await retry(async () => {
const response = await fetch('https://api.example.com/data')
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
return response.json()
}, { attempts: 3 })
console.log(result)
Syntax
typescript
function retry<T>(
fn: () => Promise<T>,
options?: {
attempts?: number
delay?: number
backoff?: boolean
onRetry?: (error: Error, attempt: number) => void
}
): Promise<T>
Parameters
fn
(function): The async function to retryoptions
(object, optional): Retry optionsattempts
(number, optional): Maximum retry attempts, defaults to 3delay
(number, optional): Retry delay in milliseconds, defaults to 1000backoff
(boolean, optional): Whether to use exponential backoff, defaults to falseonRetry
(function, optional): Callback function on retry
Return Value
Returns a Promise that resolves to the final result of the function.
Examples
Basic Retry
typescript
import { retry } from 'radash'
const result = await retry(async () => {
const response = await fetch('https://api.example.com/data')
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
return response.json()
}, { attempts: 3 })
console.log(result)
Custom Retry Count and Delay
typescript
import { retry } from 'radash'
const result = await retry(async () => {
const response = await fetch('https://api.example.com/unreliable')
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`)
}
return response.json()
}, {
attempts: 5,
delay: 2000 // 2 second delay
})
console.log('Success:', result)
Using Exponential Backoff
typescript
import { retry } from 'radash'
const result = await retry(async () => {
const response = await fetch('https://api.example.com/data')
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
return response.json()
}, {
attempts: 3,
backoff: true // Use exponential backoff
})
console.log(result)
With Retry Callback
typescript
import { retry } from 'radash'
const result = await retry(async () => {
const response = await fetch('https://api.example.com/data')
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
return response.json()
}, {
attempts: 3,
onRetry: (error, attempt) => {
console.log(`Attempt ${attempt} failed:`, error.message)
}
})
console.log('Final result:', result)
Handle Different Error Types
typescript
import { retry } from 'radash'
const result = await retry(async () => {
const response = await fetch('https://api.example.com/data')
if (response.status === 429) {
throw new Error('Rate limited')
}
if (response.status >= 500) {
throw new Error('Server error')
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
return response.json()
}, {
attempts: 5,
delay: 1000,
onRetry: (error, attempt) => {
if (error.message === 'Rate limited') {
console.log(`Rate limited, waiting before attempt ${attempt + 1}`)
} else {
console.log(`Server error on attempt ${attempt}, retrying...`)
}
}
})
Database Connection Retry
typescript
import { retry } from 'radash'
async function connectToDatabase() {
return await retry(async () => {
const connection = await createDatabaseConnection()
// Test the connection
await connection.query('SELECT 1')
return connection
}, {
attempts: 5,
delay: 2000,
backoff: true,
onRetry: (error, attempt) => {
console.log(`Database connection attempt ${attempt} failed:`, error.message)
}
})
}
const db = await connectToDatabase()
console.log('Database connected successfully')
File Operation Retry
typescript
import { retry } from 'radash'
async function readFileWithRetry(filePath: string) {
return await retry(async () => {
const fs = await import('fs/promises')
const content = await fs.readFile(filePath, 'utf-8')
return content
}, {
attempts: 3,
delay: 1000,
onRetry: (error, attempt) => {
console.log(`File read attempt ${attempt} failed:`, error.message)
}
})
}
const content = await readFileWithRetry('./config.json')
console.log('File content:', content)
API Rate Limiting
typescript
import { retry } from 'radash'
async function fetchWithRateLimit(url: string) {
return await retry(async () => {
const response = await fetch(url)
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After')
const delay = retryAfter ? parseInt(retryAfter) * 1000 : 5000
throw new Error(`Rate limited, retry after ${delay}ms`)
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
return response.json()
}, {
attempts: 3,
delay: 1000,
onRetry: (error, attempt) => {
if (error.message.includes('Rate limited')) {
const delay = parseInt(error.message.match(/\d+/)?.[0] || '5000')
console.log(`Rate limited, waiting ${delay}ms before retry`)
return delay
}
return 1000
}
})
}
const data = await fetchWithRateLimit('https://api.example.com/data')
Conditional Retry
typescript
import { retry } from 'radash'
async function processWithConditionalRetry(data: any) {
return await retry(async () => {
const response = await fetch('/api/process', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
if (!response.ok) {
const error = await response.json()
// Only retry on specific error types
if (error.code === 'TEMPORARY_ERROR' || error.code === 'RATE_LIMIT') {
throw new Error(error.message)
} else {
// Don't retry on permanent errors
throw new Error('Permanent error, not retrying')
}
}
return response.json()
}, {
attempts: 3,
delay: 1000,
onRetry: (error, attempt) => {
if (error.message.includes('Permanent error')) {
throw error // Stop retrying
}
console.log(`Retrying on attempt ${attempt}:`, error.message)
}
})
}
Timeout with Retry
typescript
import { retry } from 'radash'
async function fetchWithTimeoutAndRetry(url: string, timeout: number = 5000) {
return await retry(async () => {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), timeout)
try {
const response = await fetch(url, { signal: controller.signal })
clearTimeout(timeoutId)
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
return response.json()
} catch (error) {
clearTimeout(timeoutId)
if (error.name === 'AbortError') {
throw new Error('Request timeout')
}
throw error
}
}, {
attempts: 3,
delay: 1000,
onRetry: (error, attempt) => {
console.log(`Attempt ${attempt} failed:`, error.message)
}
})
}
const data = await fetchWithTimeoutAndRetry('https://api.example.com/slow', 3000)
Batch Processing with Retry
typescript
import { retry } from 'radash'
async function processBatchWithRetry(items: any[]) {
const results = await Promise.all(
items.map(item =>
retry(async () => {
const response = await fetch('/api/process', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(item)
})
if (!response.ok) {
throw new Error(`Processing failed: ${response.status}`)
}
return response.json()
}, {
attempts: 3,
delay: 1000,
onRetry: (error, attempt) => {
console.log(`Item processing attempt ${attempt} failed:`, error.message)
}
})
)
)
return results
}
const items = [{ id: 1 }, { id: 2 }, { id: 3 }]
const processedItems = await processBatchWithRetry(items)
Custom Retry Strategy
typescript
import { retry } from 'radash'
class CustomRetryStrategy {
private attemptCount = 0
private lastError: Error | null = null
async execute<T>(fn: () => Promise<T>): Promise<T> {
while (this.attemptCount < 5) {
try {
return await fn()
} catch (error) {
this.lastError = error as Error
this.attemptCount++
if (this.attemptCount >= 5) {
throw error
}
// Custom delay logic
const delay = this.calculateDelay()
await new Promise(resolve => setTimeout(resolve, delay))
console.log(`Retry attempt ${this.attemptCount} after ${delay}ms`)
}
}
throw this.lastError!
}
private calculateDelay(): number {
// Custom delay calculation
return Math.min(1000 * Math.pow(2, this.attemptCount - 1), 10000)
}
}
// Using custom strategy with radash retry
const customRetry = new CustomRetryStrategy()
const result = await retry(async () => {
return await customRetry.execute(async () => {
const response = await fetch('https://api.example.com/data')
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
return response.json()
})
}, { attempts: 1 }) // Only one attempt since custom strategy handles retries
Notes
- Error handling: Only retries on thrown errors
- Exponential backoff: Increases delay exponentially with each retry
- Custom delays: Can override delay with onRetry callback
- Performance: Adds overhead for retry logic
- Timeout: Consider adding timeout to prevent infinite retries
Differences from Other Functions
retry
: Handles retry logic for async operationsguard
: Protects operations but doesn't retryPromise.catch()
: Only handles errors, doesn't retryretry()
: Provides structured retry mechanism
Performance
- Time Complexity: O(n) where n is number of attempts
- Memory: Minimal additional memory usage
- Use Cases: Network requests, database operations, file operations