promise.race实现秒级超时需构造定时reject的promise与请求promise赛跑;失败后依超时、网络错误或服务端状态码智能重试,配合防抖、并发控制及abortcontroller避免资源浪费。

用 Promise.race 实现秒级超时裁决 + 重试,核心在于让「请求 Promise」和「定时拒绝 Promise」赛跑,谁先完成谁胜出;失败后可基于结果类型(超时 or 网络错误)决定是否重试,而非盲目轮询。
一、用 Promise.race 实现精准秒级超时
关键不是单纯加 setTimeout,而是构造一个「在指定毫秒后自动 reject 的 Promise」,与 API 请求 Promise 一起传入 race:
- 超时 Promise 必须是
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), ms)),不能用resolve后续再 throw —— race 只认状态变更瞬间 - 推荐封装为可复用函数,例如:
const withTimeout = (promise, ms) =>
Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error(`timeout ${ms}ms`)), ms)
)
]); - 调用时直接包裹 fetch:
withTimeout(fetch('/api/data'), 1000) —— 1 秒内没响应就报 timeout 错误
二、区分失败原因,智能触发重试
不能一遇到 reject 就重试。需判断是超时、网络中断,还是服务端明确返回 5xx/4xx —— 前两者适合重试,后者多数无意义:
- 在
catch中检查 error.message 是否含 'timeout',或用error.name === 'AbortError'(配合 AbortController 时)识别前端主动中断 - 对 fetch,还需检查
!response.ok和response.status >= 500,这类服务端错误通常不建议重试 - 示例逻辑:
async function fetchWithRetry(url, options = {}, retries = 2) {
for (let i = 0; i try {
const res = await withTimeout(fetch(url, options), 1000);
if (!res.ok && res.status >= 500) throw new Error(`Server error ${res.status}`);
return res;
} catch (err) {
if (i === retries || !/timeout|network|failed to fetch/i.test(err.message)) {
throw err;
}
// 等待 200ms 后重试,避免雪崩
await new Promise(r => setTimeout(r, 200));
}
}
}
三、高频场景下的防抖与并发控制补充
仅靠超时+重试还不够。高频请求(如搜索联想、滚动加载)需叠加策略防止“请求风暴”:
- 对同一 URL 的连续请求,用「时间窗口去重」:若上一次请求未完成,直接复用其 Promise,不发新请求
- 限制并发数:用信号量(semaphore)或队列控制同时进行的请求数 ≤ 3,其余排队 —— 避免 race 大量创建定时器拖垮主线程
- 超时阈值动态调整:首次设 800ms,若连续两次超时,下次升至 1200ms;恢复成功后逐步回调 —— 更适应网络波动
四、避坑提醒:常见失效点
很多实现看似能跑,但线上会静默失败:
-
fetch 不抛错:网络断开时 fetch 返回 rejected Promise,但 404/500 默认不会 reject,必须手动
if (!res.ok) throw … -
race 不取消原请求:超时后 fetch 仍在后台运行,可能后续触发 then 导致重复处理 —— 应搭配
AbortController主动中止 -
未清理定时器:若请求提前 resolve,超时定时器仍存在内存中 —— 用
AbortSignal.timeout()(现代浏览器)或封装时返回清理函数更稳妥 - 重试无限循环:忘记限制重试次数,或错误地把 429(限流)当超时重试,导致被服务器拉黑
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










