retryAsync.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. /**
  2. * 异步操作重试工具函数
  3. * @param {Function} asyncFn - 需要重试的异步函数
  4. * @param {Object} options - 重试配置选项
  5. * @param {number} options.maxRetries - 最大重试次数 (默认: 3)
  6. * @param {number} options.delay - 重试延迟时间(ms) (默认: 1000)
  7. * @param {string} options.backoffStrategy - 退避策略 ('fixed' | 'exponential' | 'linear') (默认: 'fixed')
  8. * @param {number} options.backoffFactor - 退避因子 (默认: 2)
  9. * @param {Function} options.shouldRetry - 自定义重试条件函数 (默认: 所有错误都重试)
  10. * @param {Function} options.onRetry - 重试回调函数
  11. * @param {number} options.timeout - 单次操作超时时间(ms)
  12. * @returns {Promise} 返回异步操作的结果
  13. */
  14. export async function retryAsync(asyncFn, options = {}) {
  15. const {
  16. maxRetries = 3,
  17. delay = 1000,
  18. backoffStrategy = 'fixed',
  19. backoffFactor = 2,
  20. shouldRetry = () => true,
  21. onRetry = () => {},
  22. timeout
  23. } = options;
  24. let lastError;
  25. for (let attempt = 0; attempt <= maxRetries; attempt++) {
  26. try {
  27. // 如果设置了超时时间,包装函数添加超时控制
  28. const result = timeout ?
  29. await withTimeout(asyncFn, timeout) :
  30. await asyncFn();
  31. return result;
  32. } catch (error) {
  33. lastError = error;
  34. // 如果是最后一次尝试,直接抛出错误
  35. if (attempt === maxRetries) {
  36. throw error;
  37. }
  38. // 检查是否应该重试
  39. if (!shouldRetry(error, attempt + 1)) {
  40. throw error;
  41. }
  42. // 执行重试回调
  43. onRetry(error, attempt + 1, maxRetries);
  44. // 计算延迟时间并等待
  45. const waitTime = calculateDelay(delay, attempt, backoffStrategy, backoffFactor);
  46. await sleep(waitTime);
  47. }
  48. }
  49. throw lastError;
  50. }
  51. /**
  52. * 计算重试延迟时间
  53. */
  54. function calculateDelay(baseDelay, attempt, strategy, factor) {
  55. switch (strategy) {
  56. case 'exponential':
  57. return baseDelay * Math.pow(factor, attempt);
  58. case 'linear':
  59. return baseDelay * (attempt + 1);
  60. case 'fixed':
  61. default:
  62. return baseDelay;
  63. }
  64. }
  65. /**
  66. * 延迟函数
  67. */
  68. function sleep(ms) {
  69. return new Promise(resolve => setTimeout(resolve, ms));
  70. }
  71. /**
  72. * 为异步函数添加超时控制
  73. */
  74. function withTimeout(asyncFn, timeoutMs) {
  75. return new Promise((resolve, reject) => {
  76. let isResolved = false;
  77. const timer = setTimeout(() => {
  78. if (!isResolved) {
  79. isResolved = true;
  80. reject(new Error(`Operation timed out after ${timeoutMs}ms`));
  81. }
  82. }, timeoutMs);
  83. asyncFn()
  84. .then(result => {
  85. if (!isResolved) {
  86. isResolved = true;
  87. clearTimeout(timer);
  88. resolve(result);
  89. }
  90. })
  91. .catch(error => {
  92. if (!isResolved) {
  93. isResolved = true;
  94. clearTimeout(timer);
  95. reject(error);
  96. }
  97. });
  98. });
  99. }