JavaScript Promise.all 失败后为什么其他请求仍可能继续
页面同时加载用户资料、权限和推荐列表时,Promise.all() 只会告诉你“这一组结果不能整体使用”,不会替你取消已经启动的请求。某一个 Promise 先 reject 后,其他 fetch 仍可能继续传输、解析,甚至在自己的回调里修改状态。要真正停止它们,必须把取消信号传给每个请求,再用批次编号挡住迟到响应。
官方地址:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Promise.all的 fail-fast 是结果语义,不是资源取消机制。- 同一批任务共享一个
AbortSignal,失败、切页或重试时由批次统一触发abort()。 - 提交 UI 状态前同时检查请求代次和取消状态,避免旧响应覆盖新数据。
先分清 Promise.all 的失败边界
Promise.all([a, b, c]) 在任意输入拒绝时立即拒绝,并把第一个拒绝原因交给调用方;这只是聚合 Promise 的状态变化。a、b、c 早已分别开始执行,聚合失败不会撤回它们已经发出的网络请求,也不会自动终止定时器、流读取或自定义异步任务。

因此,看到“页面已经进入 catch,但网络面板还有请求完成”并不矛盾。先记录每个任务的开始、成功、失败和取消,再决定是否需要取消;如果只是想拿到所有结果而允许部分失败,应考虑 Promise.allSettled(),不要用取消去掩盖业务上的部分可用。
用共享 AbortController 让取消信号到达每个请求
对于可取消的 Web API,常见做法是一次批次创建一个控制器,把它的 signal 交给所有 fetch。任一请求发生不可继续的错误时,先调用 controller.abort(),再让外层等待聚合 Promise 的结果。示例中的注释说明了取消原因和清理边界:
async function loadDashboard(urls) {
// 一个批次只创建一个控制器,让所有 fetch 共享取消信号。
const controller = new AbortController();
const { signal } = controller;
const tasks = urls.map((url) =>
fetch(url, { signal }).then((response) => {
// HTTP 失败不会自动 reject,显式抛出便于 Promise.all 统一处理。
if (!response.ok) throw new Error(`HTTP ${response.status}: ${url}`);
return response.json();
}),
);
try {
return await Promise.all(tasks);
} catch (error) {
// 失败意味着本批结果不可用,协作取消仍未结束的请求。
controller.abort();
if (error.name === "AbortError") throw error;
throw error;
}
}
这里的关键不是把 AbortController 放进 Promise.all,而是把同一个 signal 传进每一个可取消操作。取消后通常会得到名称为 AbortError 的拒绝;它代表用户切页、主动重试或批次清理,不一定代表服务器故障。生产代码应把它和真正的 HTTP、解析、权限错误分开记录。
用请求代次挡住旧批次的迟到结果
abort() 是协作式通知,不能抹掉已经进入浏览器队列、缓存或业务回调的所有工作。页面快速切换筛选条件时,旧批次即使被取消,也可能在取消前完成。为每次加载分配递增的 generation,只有仍属于当前批次的结果才允许写入状态:
let currentGeneration = 0;
let activeController = null;
async function refreshPanel(filters, render) {
// 新批次先取消旧批次,并用代次识别迟到结果。
activeController?.abort();
const controller = new AbortController();
activeController = controller;
const generation = ++currentGeneration;
try {
const result = await loadDashboard(buildUrls(filters), controller.signal);
// 取消状态与代次都要检查,防止旧请求覆盖新界面。
if (controller.signal.aborted || generation !== currentGeneration) return;
render(result);
} catch (error) {
// 用户主动切换条件不应显示成红色业务错误。
if (error.name !== "AbortError" && generation === currentGeneration) {
render({ error });
}
} finally {
// 只清理自己持有的控制器,避免误删新批次的引用。
if (generation === currentGeneration) activeController = null;
}
}
上例中 loadDashboard 需要改为接收外部 signal,或者由调用方直接创建任务;不要在内部无条件新建控制器,否则外层的取消信号无法传进去。对于非 fetch 的任务,则要在任务实现中主动监听 signal.aborted,并在合适的清理点停止工作。
按业务选择 all、allSettled 与可取消策略
选择组合器前先问一个问题:这组请求是“必须全部成功”还是“尽量收集可用结果”?前者可以使用 Promise.all,失败后通过共享信号结束剩余请求;后者使用 Promise.allSettled,让页面分别展示成功卡片和失败卡片,但仍可在用户离开页面时主动取消。
| 场景 | 组合器 | 取消策略 | 验收信号 |
|---|---|---|---|
| 权限与主体数据缺一不可 | all | 关键错误触发批次 abort | catch 后不再提交整页结果 |
| 多个独立统计卡片 | allSettled | 切页或重试时 abort | 成功卡片保留,失败卡片可解释 |
| 新筛选覆盖旧筛选 | 按依赖决定 | 新代次先取消旧代次 | 旧响应不能 render |
排查时至少覆盖四个场景:一个请求立刻失败、一个请求慢速完成、用户连续切换两次筛选、组件卸载后仍有响应返回。检查网络层是否收到取消、日志是否把 AbortError 与服务端错误分开、状态层是否验证代次。只有“聚合失败、底层任务取消、旧结果不回写”三件事同时成立,才算完成了并发请求的取消设计。

常见问题
Promise.all 失败后还能拿到其他请求的结果吗?
底层请求可能仍会完成,但聚合 Promise 不会再返回完整数组。若要逐项读取成功或失败,请改用 Promise.allSettled 或为每项单独处理。
调用 abort() 后服务器一定停止处理吗?
不一定。它主要通知浏览器和客户端停止等待或传输,服务器已接收的请求可能已经进入处理流程,服务端仍需自己的取消或幂等设计。
为什么用了 AbortController 还会出现旧数据?
取消是异步协作,响应可能在取消前完成。提交状态前增加批次编号或请求 token 检查,才能拦住迟到结果。
把 Promise.all 看成“结果聚合器”,把 AbortController 看成“取消通道”,再用代次检查保护 UI,三者职责分开后,并发请求的失败、重试和切页行为就更容易解释和测试。
Go encoding/xml 命名空间前缀变化时怎么按 URI 判断
- 上一篇
- Go encoding/xml 命名空间前缀变化时怎么按 URI 判断
- 下一篇
- Go regexp 编译正则失败时怎么把错误交给配置层
-
- 文章 · 前端 | 1小时前 |
- CSS position sticky 不生效时先检查哪个滚动容器
- 241浏览 收藏
-
- 文章 · 前端 | 5小时前 |
- CSS @container style query 和 size query 怎么区分
- 158浏览 收藏
-
- 文章 · 前端 | 7小时前 |
- Web Component 自定义元素何时适合放到 connectedCallback
- 484浏览 收藏
-
- 文章 · 前端 | 9小时前 |
- IndexedDB object store 没有索引时怎么优化按字段查询
- 476浏览 收藏
-
- 文章 · 前端 | 11小时前 | 前端 · javascript · Web Worker · JavaScript dom postMessage Web Worker
- Web Worker 不能直接改 DOM 时应该如何回传结果
- 443浏览 收藏
-
- 文章 · 前端 | 12小时前 | 前端 · javascript · Fetch API · 异步请求 · Fetch AbortController 响应体 Response.json AbortError
- AbortController 取消 fetch 后如何避免读取响应体报错
- 248浏览 收藏
-
- 文章 · 前端 | 13小时前 | 前端 · 接口排错 · Fetch API · JSON解析 · JSON.parse Fetch API Response.json Response.text
- Fetch API 读取响应 JSON 失败时怎么保留原始文本
- 339浏览 收藏
-
- 文章 · 前端 | 15小时前 | react · next.js · 表单校验 · Next.js 表单状态 Server Action useActionState
- Next.js Server Action 返回校验错误时怎么保留表单状态
- 308浏览 收藏
-
- 文章 · 前端 | 15小时前 | 前端 · 性能优化 · IntersectionObserver · 懒加载 · 懒加载 重复请求 IntersectionObserver
- IntersectionObserver 回调频繁触发时怎么减少重复加载
- 154浏览 收藏
-
- 文章 · 前端 | 17小时前 | 前端 · 缓存 · pwa · Service Worker · Service Worker Cache Storage 前端缓存更新 PWA缓存
- Service Worker 更新后旧缓存不消失怎么设计版本清理
- 127浏览 收藏
-
- 文章 · 前端 | 19小时前 |
- React useEffect 清理函数为什么会在开发模式执行两次
- 381浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 51次使用
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 201次使用
-
- C-Eval
- 深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
- 137次使用
-
- AI Prompt Library
- 探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
- 68次使用
-
- Generrated
- Generrated汇集9300+张DALL·E生成图像及对应提示词,支持查看完整图集、对比DALL·E 2与3版本差异,是AI绘图新手学习Prompt设计与获取创作灵感的实用工具。
- 49次使用
-
- Go语言对前端领域的入侵WebAssembly运行原理
- 2022-12-31 130浏览
-
- 这样需求的表单一般要这么做?
- 2023-02-17 194浏览
-
- go+gin 静态资源路由与后端api路由冲突如何解决?
- 2023-02-24 414浏览
-
- css 中的px 跟 video 中px 等同吗?
- 2023-01-09 427浏览
-
- 为什么使用了https还是可以被抓包
- 2023-02-16 480浏览
