🔄 Handle Network Failures Gracefully
Request fails. You retry immediately. Fails again. Server overwhelmed. Exponential backoff fixes this.
The Pattern
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
for (let i = 0; i <= maxRetries; i++) {
try {
const response = await fetch(url, options);
// Success
if (response.ok) {
return await response.json();
}
// Server error (5xx) or rate limit (429) - retry
if (response.status >= 500 || response.status === 429) {
if (i === maxRetries) throw new Error('Max retries reached');
// Exponential backoff: 1s, 2s, 4s, 8s...
const delay = Math.pow(2, i) * 1000;
console.log(`Retry ${i + 1} after ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
// Client error (4xx) - don't retry
throw new Error(`HTTP ${response.status}`);
} catch (error) {
if (i === maxRetries) throw error;
// Network error - retry with backoff
const delay = Math.pow(2, i) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
// Usage
try {
const data = await fetchWithRetry('/api/data');
console.log(data);
} catch (error) {
console.error('Request failed after retries:', error);
}
⏱️ Retry Timing
| Attempt | Delay | Total Time |
|---|---|---|
| 1st retry | 1 second | 1s |
| 2nd retry | 2 seconds | 3s |
| 3rd retry | 4 seconds | 7s |
| 4th retry | 8 seconds | 15s |
🎯 Advanced: Jitter
// Add randomness to prevent thundering herd const delay = Math.pow(2, i) * 1000; const jitter = Math.random() * 1000; await new Promise(resolve => setTimeout(resolve, delay + jitter));
Prevents all clients from retrying at exact same time
“API went down during peak traffic. Exponential backoff prevented retry storm from making it worse. Users saw loading states instead of errors. Service recovered gracefully.”
