配了三年 async/await,每次 Promise.all 一个挂了你都不知道其他结果怎么样了——今天 Firefox 155 把这件事用对象键彻底修了

用对象键替代数组下标,让并发 Promise 的结果永远对得上名字。Firefox 155 首发 Promise.allKeyed() 和 Promise.allSettledKeyed(),TC39 Await Dictionary 提案进入实现阶段。


你一定写过这种代码:

“`js
const [user, posts, comments] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchComments(),
]);
“`

三个月后产品说「评论列表要放到用户信息前面」,你调了一下数组顺序——然后线上开始报 `user is undefined`。这种 bug 往往在 review 里看不出来,因为它不是语法错,是逻辑错,是位置依赖悄悄埋进去的地雷。

Firefox 155(2026年9月1日稳定版)把这件事彻底修了,引入了 `Promise.allKeyed()` 和 `Promise.allSettledKeyed()`,来自 TC39 Await Dictionary 提案(当前 Stage 2.7,API 设计已冻结)。

Promise.allKeyed() — 位置依赖的终结者

“`js
const { user, posts, comments } = await Promise.allKeyed({
user: fetchUser(),
posts: fetchPosts(),
comments: fetchComments(),
});
“`

传入一个对象,返回一个同键名的对象。调换属性顺序?完全不影响结果,因为键名本身就是结果的位置标记。你甚至可以在对象里混用普通值和 Promise:

“`js
const { data, cached } = await Promise.allKeyed({
data: fetchData(),
cached: getFromMemory(), // 普通值,会被自动包装
});
“`

Promise.allSettledKeyed() — 错误不短路

对应 `Promise.allSettled`,每个键的值是 `{ status: fulfilled | rejected, value | reason }` 结构:

“`js
const results = await Promise.allSettledKeyed({
user: fetchUser(),
posts: fetchPosts(),
config: fetchConfig(),
});

for (const [key, result] of Object.entries(results)) {
if (result.status === fulfilled) {
console.log(`${key} 成功:`, result.value);
} else {
console.error(`${key} 失败:`, result.reason);
}
}
“`

一个 Promise reject 了?不会短路,剩下的继续跑,每个都能拿到结果。这是 `Promise.all` 完全做不到的事。

为什么会是对象而不是数组?

`Promise.all` 的根问题是:结果的位置靠数组下标标记,而下标在代码重构时完全不语义化。你看着 `results[2]` 完全不知道它对应哪个接口,但 `results.config` 一目了然。

TC39 Await Dictionary 提案从 Stage 2.7 开始锁定这个语义——结果对象是 null prototype 的(`Object.create(null)`),不会有原型链上的意外属性污染。

浏览器支持现状

  • Firefox 155+(2026年9月1日稳定版):✅ 完整支持
  • Chrome/Safari:❌ 尚未支持
  • Polyfill:`core-js/proposals/await-dictionary` 或 `es-toolkit/promise`

“`js
// 渐进增强写法
const allKeyed = Promise.allKeyed ?? require(promise.allkeyed);
const { a, b } = await allKeyed({ a: fetchA(), b: fetchB() });
“`

下一步:现在就改掉这三个场景

如果你现在有代码满足以下条件,立刻就可以用:

  1. 多接口并行请求:把 `Promise.all([fetchA(), fetchB()])` 替换成 `Promise.allKeyed({ a: fetchA(), b: fetchB() })`,结果解构用键名而非数组下标
  2. 需要逐个处理错误:`Promise.all` 一个 reject 全挂 → `Promise.allSettledKeyed` 每个都有状态
  3. Server Components / Data Loader:服务端数据获取场景,对象语义天然匹配 `{ user, posts }` 的数据加载模式

结论

Firefox 155 这次首发不是浏览器又加了一个 API——它补全了 Promise 组合子十年来的结构性缺陷:数组下标不该是并发结果的位置标记,键名才是。等 Chrome 和 Safari 也跟上来(预计 2026 年底),这个写法会成为新的默认。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 16 阅读