← back to Animate Museum Posts
src/utils/retry.js
73 lines
/**
* Retry utility with exponential backoff
*/
export async function withRetry(fn, options = {}) {
const {
maxRetries = 3,
initialDelay = 1000,
maxDelay = 30000,
backoffFactor = 2,
onRetry = null
} = options;
let lastError;
let delay = initialDelay;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
if (attempt === maxRetries) {
break;
}
console.log(`Attempt ${attempt}/${maxRetries} failed: ${error.message}`);
console.log(`Retrying in ${delay}ms...`);
if (onRetry) {
onRetry(error, attempt, delay);
}
await sleep(delay);
// Exponential backoff with max cap
delay = Math.min(delay * backoffFactor, maxDelay);
}
}
throw lastError;
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Wrap an async function to never throw - returns null on error
*/
export function safeCall(fn, fallback = null) {
return async (...args) => {
try {
return await fn(...args);
} catch (error) {
console.error(`Safe call failed: ${error.message}`);
return fallback;
}
};
}
/**
* Wrap with timeout
*/
export function withTimeout(promise, ms, message = 'Timeout') {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error(message)), ms)
)
]);
}