async/await 本身不提供超时和重试能力,需手动编排:超时推荐 abortcontroller(真正中止请求)或 promise.race(需清理残留);重试须指数退避、熔断与错误分类;错误处理需每层 try/catch、fetch 检查 response.ok、并发选对聚合方式。

async/await 本身不带超时和重试能力,得靠手动编排控制流。关键不是写得“像同步”,而是让失败可预期、可中断、可恢复。
超时:必须主动加,不能依赖语法
原生 await 不会自动超时,等一个卡住的 Promise 可能永远挂起。稳妥做法是用 AbortController(推荐)或 Promise.race 包装原始操作:
- fetch 场景优先用 AbortSignal:创建 controller,传 signal 给 fetch,超时后调
controller.abort(),真正中止网络请求,避免资源残留; - 通用 Promise 超时可用 race 封装:比如
Promise.race([promise, timeoutPromise]),但要注意 race 成功后原始 promise 仍可能 resolve/reject,需额外清理或忽略; - 超时阈值要分级设定——读接口建议 5–8 秒,写操作或含外部依赖的可放宽到 12–15 秒,别一刀切设 3 秒;
- 超时错误最好附带上下文,比如接口路径、参数精简摘要,方便定位是哪一环慢。
重试:不能只循环,得讲策略
简单 for 循环重试容易雪崩,尤其下游已抖动时。健壮重试需兼顾退避、熔断和错误分类:
- 用指数退避:第 1 次等 100ms,第 2 次 200ms,第 3 次 400ms……避免密集冲击;
- 限制最大次数(通常 3–5 次),且区分错误类型——连接拒绝、503 可重试;401、403、400 直接终止;
- 引入简易熔断:比如连续 3 次失败后,暂停 30 秒再恢复,期间返回兜底响应或抛明确熔断异常;
- 每次重试记日志:标清第几次、错误码、耗时、是否熔断,便于判断是偶发抖动还是服务真不可用。
错误处理:每一层都得自己守门
await 不会自动捕获异常,没 try/catch 的 rejection 会一路冒泡,可能崩掉整个流程:
- 每个关键 await 前加独立 try/catch,防止一错全错;
- 非核心依赖(如埋点、日志上报)可用 silent catch:
await someLog().catch(() => {}); - fetch 返回 4xx/5xx 默认不 throw,必须手动检查
response.ok或response.status再 reject; - 拒绝值别用字符串,比如
Promise.reject('网络失败'),要用带 code、message、stack 的 Error 实例,方便后续分类和监控。
并发协调:选对聚合方式,避免竞态
多个异步任务并行时,Promise 方法选错会导致逻辑错乱或内存泄漏:
- 强顺序依赖(B 需 A 结果)不用
Promise.all,改用串行 await 或 reduce 链式调用; - 批量请求允许部分失败?用
Promise.allSettled,它返回每个结果的状态,不会因一个失败就中断全部; - 大量请求需控量?实现并发池(如最多同时跑 5 个),避免打爆下游或耗尽内存;
- for-await-of 适合处理可迭代的异步数据流(如 EventSource、ReadableStream),注意它隐含串行语义。











