← 中文资源

简体中文版本

Retry 操作包装器与错误过滤

用 retry 包把不稳定操作包装成自动重试的 Promise:谓词式错误判定、指数因子、随机抖动与外部中止信号组合。

阅读英文原文

示例说明

retry 包提供的是操作级重试原语而不是 HTTP 专用封装,适合包任意可能失败的异步动作。operation 对象维护尝试次数与退避节奏,retry 方法既是谓词也是执行器——返回 true 表示已安排下次尝试。randomize 给退避加入随机因子,防止多个客户端同步重试形成惊群。

技术要点

  • factor 与 min/maxTimeout 构成指数退避区间
  • randomize 打散多客户端同步重试
  • mainError 返回最后一次失败供上层判断

代码解读

fetchWithRetry 把 operation 包进 Promise,attempt 回调里执行真正的请求:5xx 被转换为错误参与重试判定,成功则 resolve。catch 分支调用 operation.retry(err),不可重试或次数耗尽时返回 false,此时用 mainError 拒绝 Promise,把最后一次的真实错误交给调用方。

使用建议

signal 透传让调用方可以随时取消整个重试链。minTimeout 不要低于下游的恢复周期,否则前几次尝试必然失败。把可重试错误类型收敛成白名单谓词,避免对逻辑错误做无谓重试。

代码示例

import retry from 'retry';

async function fetchWithRetry(url, { signal } = {}) {
  const operation = retry.operation({
    retries: 5,
    factor: 2,
    minTimeout: 500,
    maxTimeout: 8000,
    randomize: true,
  });

  return new Promise((resolve, reject) => {
    operation.attempt(async (currentAttempt) => {
      try {
        const res = await fetch(url, { signal });
        if (res.status >= 500) throw new Error('upstream ' + res.status);
        resolve(await res.json());
      } catch (err) {
        if (operation.retry(err)) return;
        reject(operation.mainError());
      }
    });
  });
}

const data = await fetchWithRetry('https://api.example.com/pricing');
console.log('plans:', data.plans.length);