112 lines
3.3 KiB
JavaScript
112 lines
3.3 KiB
JavaScript
/**
|
|
* 异步操作重试工具函数
|
|
* @param {Function} asyncFn - 需要重试的异步函数
|
|
* @param {Object} options - 重试配置选项
|
|
* @param {number} options.maxRetries - 最大重试次数 (默认: 3)
|
|
* @param {number} options.delay - 重试延迟时间(ms) (默认: 1000)
|
|
* @param {string} options.backoffStrategy - 退避策略 ('fixed' | 'exponential' | 'linear') (默认: 'fixed')
|
|
* @param {number} options.backoffFactor - 退避因子 (默认: 2)
|
|
* @param {Function} options.shouldRetry - 自定义重试条件函数 (默认: 所有错误都重试)
|
|
* @param {Function} options.onRetry - 重试回调函数
|
|
* @param {number} options.timeout - 单次操作超时时间(ms)
|
|
* @returns {Promise} 返回异步操作的结果
|
|
*/
|
|
export async function retryAsync(asyncFn, options = {}) {
|
|
const {
|
|
maxRetries = 3,
|
|
delay = 1000,
|
|
backoffStrategy = 'fixed',
|
|
backoffFactor = 2,
|
|
shouldRetry = () => true,
|
|
onRetry = () => {},
|
|
timeout
|
|
} = options;
|
|
|
|
let lastError;
|
|
|
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
try {
|
|
// 如果设置了超时时间,包装函数添加超时控制
|
|
const result = timeout ?
|
|
await withTimeout(asyncFn, timeout) :
|
|
await asyncFn();
|
|
|
|
return result;
|
|
} catch (error) {
|
|
lastError = error;
|
|
|
|
// 如果是最后一次尝试,直接抛出错误
|
|
if (attempt === maxRetries) {
|
|
throw error;
|
|
}
|
|
|
|
// 检查是否应该重试
|
|
if (!shouldRetry(error, attempt + 1)) {
|
|
throw error;
|
|
}
|
|
|
|
// 执行重试回调
|
|
onRetry(error, attempt + 1, maxRetries);
|
|
|
|
// 计算延迟时间并等待
|
|
const waitTime = calculateDelay(delay, attempt, backoffStrategy, backoffFactor);
|
|
await sleep(waitTime);
|
|
}
|
|
}
|
|
|
|
throw lastError;
|
|
}
|
|
|
|
/**
|
|
* 计算重试延迟时间
|
|
*/
|
|
function calculateDelay(baseDelay, attempt, strategy, factor) {
|
|
switch (strategy) {
|
|
case 'exponential':
|
|
return baseDelay * Math.pow(factor, attempt);
|
|
case 'linear':
|
|
return baseDelay * (attempt + 1);
|
|
case 'fixed':
|
|
default:
|
|
return baseDelay;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 延迟函数
|
|
*/
|
|
function sleep(ms) {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
|
|
/**
|
|
* 为异步函数添加超时控制
|
|
*/
|
|
function withTimeout(asyncFn, timeoutMs) {
|
|
return new Promise((resolve, reject) => {
|
|
let isResolved = false;
|
|
|
|
const timer = setTimeout(() => {
|
|
if (!isResolved) {
|
|
isResolved = true;
|
|
reject(new Error(`Operation timed out after ${timeoutMs}ms`));
|
|
}
|
|
}, timeoutMs);
|
|
|
|
asyncFn()
|
|
.then(result => {
|
|
if (!isResolved) {
|
|
isResolved = true;
|
|
clearTimeout(timer);
|
|
resolve(result);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
if (!isResolved) {
|
|
isResolved = true;
|
|
clearTimeout(timer);
|
|
reject(error);
|
|
}
|
|
});
|
|
});
|
|
} |