JavaScript请求重试应采用指数退避加截断抖动策略:第n次失败后等待Math.random()*2ⁿ⁻¹×1000ms,上限约30秒,避免同步重试冲击下游服务。

在 JavaScript 请求重试中加入随机抖动(jitter),核心是让每次重试的等待时间不固定,打破同步重试节奏,从而分散请求峰值、缓解下游服务压力。关键不是单纯加个 Math.random(),而是采用带边界的指数退避 + 随机抖动策略。
用指数退避 + 截断抖动(Truncated Jitter)
这是最常用且效果稳定的方式:先按 2ⁿ 增长基础等待时间,再在 [0, 当前基础延迟] 范围内叠加随机偏移,避免所有客户端在同一时刻重试。
- 第 1 次失败后等待:
Math.random() * 1000ms(即 0–1s) - 第 2 次失败后等待:
Math.random() * 2000ms(即 0–2s) - 第 3 次失败后等待:
Math.random() * 4000ms(即 0–4s) - 以此类推,上限建议设为 30s 左右,防止过长卡顿
代码示例:fetch 封装支持抖动重试
以下是一个轻量实用的封装,使用 setTimeout 控制延迟,自动注入抖动:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
async function fetchWithJitter(url, options = {}, maxRetries = 3) {
let lastError;
for (let i = 0; i <= maxRetries; i++) {
try {
const res = await fetch(url, options);
if (res.ok) return res;
throw new Error(`HTTP ${res.status}`);
} catch (err) {
lastError = err;
if (i === maxRetries) break;
<pre class="brush:php;toolbar:false;"> // 指数退避基础值:1s, 2s, 4s...
const baseDelay = Math.pow(2, i) * 1000;
// 截断抖动:0 ~ baseDelay 之间的随机毫秒
const jitter = Math.random() * baseDelay;
// 加个简单日志便于调试
console.log(`Retry ${i + 1}/${maxRetries}, waiting ~${Math.round(jitter)}ms`);
await new Promise(r => setTimeout(r, jitter));
}} throw lastError; }
进阶建议:避免全量抖动导致过早重试
纯 [0, base] 抖动可能让部分请求几乎立刻重试,加剧瞬时压力。更稳妥的做法是采用「全抖动(Full Jitter)」或「等概率区间抖动」:
立即学习“Java免费学习笔记(深入)”;
-
全抖动:等待时间 =
Math.random() * baseDelay(如上例)——简单有效,推荐默认使用 -
去零抖动:保证最小延迟,例如
100 + Math.random() * (baseDelay - 100),避免首重试太激进 -
带上限的均匀抖动:比如限制最大抖动为 5s,即使 baseDelay 是 16s,也只取
Math.random() * 5000
别忘了配合其他熔断与降级手段
抖动只是重试优化的一环,单独使用效果有限:
- 加上请求超时(
AbortSignal.timeout()),防止卡死 - 对连续失败做简单计数或滑动窗口限频,触发临时熔断
- 非核心请求可降级为本地缓存或空数据,减少真实调用
- 服务端配合开启限流(如令牌桶)、返回 Retry-After 头,前端可优先遵循

















