retry函数需统一封装异步重试逻辑,支持配置重试次数、延迟策略(含指数退避与随机抖动)、错误判定规则及成功/失败回调,确保错误不被吞、堆栈完整、终止条件明确。

直接用 try-catch 做异步重试容易出错,关键不是套壳,而是控制重试时机、次数和失败兜底。核心是把“执行 + 捕获 + 判定 + 延迟 + 循环”串成可配置的逻辑链。
封装成可复用的 retry 函数
不要在每个业务里手写 while + setTimeout,统一抽成函数,接收异步操作、最大重试次数、间隔时间和错误判定规则:
- 用 async/await 配合 for 循环控制重试次数,避免回调地狱
- 每次失败后用 await new Promise(resolve => setTimeout(resolve, delay)) 实现可控延迟
- 允许传入 shouldRetry(error) 函数,只对特定错误(如网络超时、503)重试,跳过参数错误等不可恢复异常
处理 Promise.reject 和 throw 的一致性
异步操作可能返回 rejected Promise,也可能直接 throw,retry 函数内部要用 try/catch 包裹 await 执行,确保两种错误都能被捕获:
- 不要写 await fn().catch(...),这会吞掉错误且无法触发重试逻辑
- 正确方式:try { const res = await fn(); return res; } catch (e) { if (shouldRetry(e)) continue; else throw e; }
- 这样能保留原始堆栈,也方便上层做统一错误分类
支持指数退避和随机抖动
连续重试可能加剧服务压力,基础版用固定间隔,生产环境建议加入退避策略:
- 第 n 次重试延迟 = baseDelay * 2ⁿ,例如 100ms → 200ms → 400ms
- 叠加 0–100ms 随机抖动,防止大量请求在同一时刻重试打爆下游
- 可在 retry 函数中通过选项开启,不侵入业务代码
明确成功与失败的出口
重试不是无限循环,必须定义清晰的终止条件:
- 任意一次成功就立即 return 结果,不再继续
- 重试耗尽后,抛出最后一次错误,并附带重试统计(如 “failed after 3 attempts”),便于排查
- 可选支持 onSuccess / onFailure 回调,用于日志埋点或监控上报











