用Promise.allSettled替代Promise.all以保留所有请求结果,对每个fetch手动检查响应状态并抛出异常,控制并发数量并支持AbortSignal取消请求,对瞬态错误采用指数退避重试并设置降级方案,从而提升系统容错能力。
async/await并发请求异常处理方法
作者:技术拾光
时间:2026-07-25
浏览:326
文章目录
在处理 async/await 并发请求的异常时,最常见的误区是“要么全成功才继续,要么一错就中断”。真正的关键在于让系统具备容错能力——区分失败类型、保留部分结果、可控重试,同时避免未卸载组件的副作用。下面从几个核心环节展开。

使用 Promise.allSettled 替代 Promise.all
为什么不要死磕 Promise.all?它的毛病是“一拒即停”——只要有一个请求失败,其他 pending 请求的结果全被丢弃,而且你无法获取它们。而 Promise.allSettled 更为可靠,它会等待所有请求出结果,然后返回一个数组,每个对象包含 status("fulfilled" 或 "rejected")、value(成功值)或 reason(错误原因)。
- 特别适合“尽力而为”的场景:批量提交表单、并行拉取用户资料、聚合监控指标。不需要因为某个模块挂掉就放弃整个页面。
- 示例:
const results = await Promise.allSettled([fetch('/user/1'), fetch('/user/2')]);
results.forEach((r, i) => {
if (r.status === 'fulfilled') console.log(`第${i+1}个成功`, r.value);
else console.warn(`第${i+1}个失败`, r.reason);
});
对每个 fetch 做响应体校验
有一个常见陷阱:HTTP 状态码不是 2xx 时,fetch 并不会自动 reject。它只在网络层出错(如 DNS 解析失败、连接被拒绝)时才会抛出异常,而 401、500、404 等都会正常 resolve。这导致后续调用 .json() 时可能直接报错,或拿到空数据引发连锁崩溃。
- 解决办法:在
await fetch()之后立即检查res.ok,如果为 false 就手动 throw 一个错误。这样业务逻辑就能统一捕获。 - 可以封装成一个通用函数:
async function safeFetch(url) { const res = await fetch(url); if (!res.ok) throw new Error(`${res.status} ${res.statusText}`); return res; } - 配合
allSettled使用,网络错误、服务端错误、权限错误全都会被收进reason字段,方便分类统计和后续处理。
控制并发数量并支持取消请求
假设一口气往 allSettled 里丢 100 个请求,后端可能直接被压垮,CDN 也可能触发限流。更严重的是,用户切换页面后,那些还在运行的请求可能触发 setState 报错,造成内存泄漏。
- 推荐做法:使用
Promise.mapLimit(如p-map库)或手写分批执行,每次最多并发 6 个。这样既不会压垮后端,也不会让浏览器卡死。 - 同时,为每个
fetch传入AbortSignal,在组件卸载时(React 的useEffectcleanup,Vue 的onBeforeUnmount)调用controller.abort()取消请求。 - 示例:
const controller = new AbortController();
await fetch(url, { signal: controller.signal });
// 卸载时调用 controller.abort()
按需重试与降级策略
不是所有失败都值得重试。DNS 解析失败重试十次也是徒劳;超时可以用指数退避重试两三次;404 直接放弃即可。
- 对 transient 错误(如
NetworkError、503、timeout)做指数退避,最多试 2–3 次。退避时间不要写死,可以用 Math.pow(2, retryCount) 等算法。 - 给关键请求设置 fallback:头像加载失败显示默认图标,配置项获取失败使用 localStorage 的缓存值。这样用户体验不会因一个接口报错而崩溃。
- 注意:不要在
allSettled的回调里嵌套await重试——这会破坏并发性。最好在发起请求前决定是否重试,或直接使用retry-axios等成熟封装。
实用工具推荐
电子包浆
使用 ›绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
图片压缩
使用 ›选择画质和输出格式,预览压缩前后的大小。