指数退避重试通过倍增延迟(如100ms→200ms→400ms)避免雪崩,需支持可控重试次数、随机抖动、abortcontroller取消、超时控制、错误分类(仅重试5xx/网络异常)、可观测日志及通用封装。

用指数退避实现请求重试,核心是让失败后的等待时间随重试次数成倍增长(如 100ms → 200ms → 400ms),避免雪崩式重试、减轻服务压力,同时兼顾用户体验和成功率。关键不在“写个 for 循环”,而在于可控性、可中断、可观察、不阻塞主线程。
基础结构:Promise + setTimeout + 递归
不用 async/await 做死循环重试,而是用递归调用 Promise 链,每次失败后计算下一次延迟并重新发起请求:
- 定义最大重试次数、初始延迟(baseDelay)、退避因子(通常为 2)
- 每次失败后,延迟 = baseDelay × (factor ^ retryCount),加随机抖动(jitter)防同步冲击
- 用 setTimeout 包装 resolve/reject,确保异步等待不阻塞
支持取消与超时:用 AbortController 和 race
真实场景中用户可能离开页面、切换 tab 或主动取消操作。必须支持中断重试链:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 创建 AbortController 实例,把 signal 传给 fetch 或其他请求方法
- 用 Promise.race([fetch(...), timeoutPromise]) 控制单次请求超时(如 8s)
- 在重试前检查 signal.aborted,若已取消则直接 reject
错误分类处理:不是所有失败都该重试
网络超时、503 服务不可用可以重试;400 参数错误、401 未登录、404 资源不存在应立即失败:
- 拦截响应状态码:只对 0(网络异常)、5xx、部分 429 等重试
- 捕获 fetch 抛出的 TypeError(如“Failed to fetch”)视为网络层失败
- 自定义判断逻辑可作为参数传入,例如 isRetryable: (error, response) => boolean
可观测性与调试友好:记录重试行为
上线后需知道“重试是否生效?卡在哪次?延迟是否合理?”:
- 每次重试前打印日志:retry ${n}/${max},delay: ${delay}ms,reason: ${error.message}
- 暴露 retryCount、lastError、elapsedTime 等字段到最终 reject 的 error 对象上
- 可选:通过回调 onRetry: (opts) => {} 让业务方埋点或触发 UI 提示(如“正在重试…”)
不复杂但容易忽略:抖动(jitter)能显著降低集群重试共振风险,简单做法是 delay *= 0.5 + Math.random() * 0.5;另外别把重试逻辑硬编码进每个 API 调用,封装成通用函数或自定义 Hook(React)更利于维护。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










