你以为 async 并发只能靠数组拆包?Firefox 155 用对象把这件事彻底原生化了
写过并发请求的人都踩过这个坑——Promise.all 用数组传,拆包全靠索引对应,一旦你往函数里加了某个请求,整个结果数组的顺序就变了,要么成功要么报错,你得小心翼翼地对齐输入输出的位置。
这在实际项目里是真实的维护噩梦。
Promise.allSettled() 用数组解决了一部分问题——它不会因为一个 reject 就全盘皆输,而是等所有请求都结束才返回,每个结果带上 status 告诉你谁成了谁崩了。但它依然用数组:结果按索引对应输入,一旦顺序变了,拆包就对不上了。
Firefox 155 今天把这个用对象彻底原生化了。
数组的问题在哪
看这个典型场景:
const results = await Promise.allSettled([
fetchUser(userId),
fetchPosts(userId),
fetchComments(userId),
]);
// 你得靠索引对应:0=用户,1=帖子,2=评论
const user = results[0].status === 'fulfilled' ? results[0].value : null;
const posts = results[1].status === 'fulfilled' ? results[1].value : [];
某天产品说要加上关注列表,你往数组里插了一个 fetchFollows(),插到第二个位置——帖子和评论的索引全变了,拆包的地方不跟着改就是 bug。这就是用索引对应语义的代价。
Promise.allSettledKeyed() 怎么解决这个问题
Firefox 155(2026-09-01)新增了这个 API,TC39 Stage 1。它和 Promise.allSettled() 的行为完全一样:等所有 promise 都结束才返回,不会因为一个 reject 就提前跑路。区别在于它用对象做输入输出:
const results = await Promise.allSettledKeyed({
user: fetchUser(userId),
posts: fetchPosts(userId),
comments: fetchComments(userId),
});
// 直接通过 key 拿结果,不需要数索引
const user = results.user.status === 'fulfilled' ? results.user.value : null;
const posts = results.posts.status === 'fulfilled' ? results.posts.value : [];
const comments = results.comments.status === 'fulfilled' ? results.comments.value : [];
结果对象和输入对象的 key 一一对应。往里面加一个 follows: fetchFollows(userId),结果对象自动带上 results.follows,拆包的地方一行不用动。重构安全,代码可读,语义清晰。
Promise.allKeyed() 也来了
和数组版 Promise.all → Promise.all 的关系一样,Promise.allKeyed() 是对象版的 Promise.all:输入一个 promise 对象,返回一个 promise,任意一个 reject 就整体 reject。它解决的是并发启动时传参的顺序问题——对象 key 直接对应结果,不需要记住「第一个参数是用户,第二个是帖子」。
// 语义自解释,不需要注释说明顺序
const { user, posts } = await Promise.allKeyed({
user: fetchUser(userId),
posts: fetchPosts(userId),
});
当前浏览器支持情况
Firefox 155(2026-09-01)刚刚稳定支持,Chrome / Safari / Edge 暂时没有上线。如果你现在就想在项目里用,npm 上有完整的 polyfill:
// 需要先全局安装 es6-shim
// npm install es6-shim
require('es6-shim');
const allKeyed = require('promise.allkeyed');
allKeyed({
user: fetchUser(userId),
posts: fetchPosts(userId),
}).then(results => {
// ...
});
什么场景该用哪个
用 Promise.allKeyed(): 多个请求必须全部成功才能继续,任何一个失败意味着整个操作失败。比如同时获取渲染页面所需的骨架数据。
用 Promise.allSettledKeyed(): 多个请求相互独立,部分失败可以接受,返回部分数据比什么都不返回更好。比如仪表盘面板、动态内容聚合、用户可选的增值服务。
两个 API 可以组合:用 allKeyed 保证关键路径,用 allSettledKeyed 处理可选增强。
三步上手
第一步,看你现在用 allSettled 的地方——如果结果拆包靠注释「0 是用户,1 是帖子」,就该换了。
第二步,把数组改成对象,索引拆包改成 key 拆包。
第三步,检查有没有同时失败且必须全成功的场景,是的话切到 allKeyed,不是的话继续用 allSettledKeyed。Firefox 155 已经原生支持,其他浏览器用 polyfill 兜底。
评论区
登录后可评论。