丁同亚的博客
夺朱非正色

一个函数解决90%的异步请求稳定性问题

 * 异步重试函数
 * @param {Function} asyncFn - 要执行的异步函数
 * @param {number} maxRetries - 最大重试次数(包括首次调用)
 * @param {number} retryInterval - 重试间隔(毫秒)
 * @returns {Promise<any>} - 返回异步函数的结果或抛出错误
 */
async function retryAsync(asyncFn, maxRetries = 3, retryInterval = 1000) {
  // 验证参数
  if (typeof asyncFn !== 'function') {
    throw new TypeError('第一个参数必须是函数');
  }
  
  if (!Number.isInteger(maxRetries) || maxRetries < 1) {
    throw new TypeError('最大重试次数必须是正整数');
  }
  
  if (!Number.isInteger(retryInterval) || retryInterval < 0) {
    throw new TypeError('重试间隔必须是非负整数');
  }
  
  let lastError;
  
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      // 尝试执行异步函数
      const result = await asyncFn();
      console.log(`第 ${attempt} 次尝试成功`);
      return result;
    } catch (error) {
      lastError = error;
      console.warn(`第 ${attempt} 次尝试失败: ${error.message}`);
      
      // 如果是最后一次尝试,不再等待
      if (attempt === maxRetries) {
        console.error(`已达到最大重试次数 (${maxRetries}),操作失败`);
        break;
      }
      
      // 等待指定间隔后重试
      console.log(`等待 ${retryInterval}ms 后重试...`);
      await sleep(retryInterval);
    }
  }
  
  // 所有尝试都失败,抛出最后的错误
  throw lastError;
}

/**
 * 睡眠函数,用于等待指定时间
 * @param {number} ms - 等待的毫秒数
 * @returns {Promise<void>}
 */
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// 使用示例
async function exampleUsage() {
  // 模拟一个可能失败的异步函数
  let callCount = 0;
  const unreliableAsyncFunction = async () => {
    callCount++;
    console.log(`调用异步函数,第 ${callCount} 次`);
    
    // 模拟失败:前两次失败,第三次成功
    if (callCount < 3) {
      throw new Error(`模拟失败,第 ${callCount} 次调用`);
    }
    
    return { success: true, data: '这是返回的数据', callCount };
  };
  
  try {
    // 尝试最多5次,每次间隔2秒
    const result = await retryAsync(unreliableAsyncFunction, 5, 2000);
    console.log('最终成功:', result);
    return result;
  } catch (error) {
    console.error('最终失败:', error.message);
    throw error;
  }
}

// 测试
exampleUsage().catch(() => {
  console.log('主流程捕获到错误');
});

// 更简洁的版本(去掉日志和参数验证)
async function retryAsyncSimple(asyncFn, maxRetries = 3, retryInterval = 1000) {
  let lastError;
  
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await asyncFn();
    } catch (error) {
      lastError = error;
      
      if (attempt === maxRetries) break;
      
      if (retryInterval > 0) {
        await new Promise(resolve => setTimeout(resolve, retryInterval));
      }
    }
  }
  
  throw lastError;
}

// 支持指数退避策略的版本
async function retryAsyncWithBackoff(
  asyncFn, 
  maxRetries = 3, 
  baseInterval = 1000,
  maxInterval = 10000
) {
  let lastError;
  
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await asyncFn();
    } catch (error) {
      lastError = error;
      
      if (attempt === maxRetries) break;
      
      // 指数退避:等待时间逐渐增加
      const delay = Math.min(
        baseInterval * Math.pow(2, attempt - 1),
        maxInterval
      );
      
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
  
  throw lastError;
}

// 导出函数(如果是模块环境)
// export { retryAsync, retryAsyncSimple, retryAsyncWithBackoff };```
posted on 2026-01-23 13:16  丁同亚的博客  阅读(1)  评论(0)    收藏  举报