async/await 下处理超时的核心是主动终止 Promise 并抛出明确异常,常用 Promise.race 组合定时 reject 的 Promise 实现;可封装 timeout 工具函数复用;fetch 无原生 timeout,需用 AbortSignal.timeout()(兼容性检查)或 race 回退;捕获后应降级、重试、监控并友好提示。

async/await 模式下处理超时错误,核心思路是:**主动终止长时间未响应的 Promise,并让 await 捕获一个明确的超时异常**。JavaScript 原生不提供直接的 Promise 超时语法,但可通过组合 Promise.race 或封装工具函数实现。关键不是等它失败,而是“主动放弃”。
用 Promise.race 实现超时控制
这是最常用、语义清晰的方式:把目标异步操作和一个「定时拒绝」的 Promise 放进 race,谁先完成(无论成功或失败)就取谁的结果。
- 创建一个在指定毫秒后
reject的超时 Promise,例如:new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 5000)) - 与真实请求 Promise 一起传入
Promise.race([fetch(...), timeoutPromise]) - await 这个 race 结果,超时会直接抛出错误,被 try/catch 捕获
封装成可复用的 timeout 函数
避免每次手写 race,推荐封装一个通用工具:
- 定义
timeout(promise, ms, message = 'Request timeout') - 内部返回
Promise.race([promise, new Promise(...)]) - 调用时写成:
const data = await timeout(fetch('/api/data'), 3000); - 这样既保持 await 风格,又把超时逻辑抽离,便于统一配置和测试
注意 fetch 自身的超时限制
fetch API 本身不支持 timeout 选项(截至 2026 年标准仍未加入),所以不能只靠 fetch(..., { signal }) 就万事大吉——AbortSignal.timeout() 是较新特性(Chrome 120+ / Safari 17.4+ 支持),使用前需检查兼容性或搭配 polyfill:
- 支持时可写:
const controller = AbortSignal.timeout(5000); const res = await fetch(url, { signal: controller }); - 不支持时,仍应回退到
Promise.race方案,确保行为一致 - 注意:abort 会触发
AbortError,建议在 catch 中识别并归一化为业务可理解的超时错误
结合业务做降级与重试
捕获超时错误后,别只打印日志——要赋予实际意义:
- 返回默认值(如空数组、占位数据),保证 UI 不崩溃
- 触发轻量级重试(最多 1 次),用指数退避避免雪崩
- 上报监控:记录超时接口、耗时、频率,用于性能优化
- 用户提示:“网络稍慢,请稍候” 比 “未知错误” 更友好

















