写过前端的人都踩过这个坑——每次并发请求等结果,解构赋值后拿到的变量顺序一变代码就悄悄 break 了,Firefox 155 今天把这件事用两个新 API 彻底原生化了
写过前端的人都踩过这个坑——每次并发请求等结果,解构赋值后拿到的变量顺序一变代码就悄悄 break 了,Firefox 155 今天把这件事用两个新 API 彻底原生化了。
以前写并发请求是这样的:
const [user, posts, settings] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
fetch('/api/settings').then(r => r.json()),
]);
renderDashboard(user, posts, settings);
这行代码里有个隐藏的脆弱点:数组是按位置对应的,user 在第 0 位,posts 在第 1 位,settings 在第 2 位。一旦哪天产品加了需求变成 [settings, user, posts],解构赋值里的三个变量名必须跟着改,否则 renderDashboard(settings, user, posts) 传入的参数名和实际内容就对不上了——TypeScript 不报错,代码跑得通,只是结果悄悄错了。
这其实是 Promise.all 设计上的一个历史遗留:它接受 iterable(数组),结果按数组索引顺序返回,所以调用方必须记住哪个位置对应哪个数据。
Firefox 155 带来了两个新方法
Promise.allKeyed() 和 Promise.allSettledKeyed(),接受一个对象而不是数组,返回值也是一个对象,key 保持不变:
const result = await Promise.allKeyed({
user: fetch('/api/user').then(r => r.json()),
posts: fetch('/api/posts').then(r => r.json()),
settings: fetch('/api/settings').then(r => r.json()),
});
// 直接按 key 访问,不再按位置
renderDashboard(result.user, result.posts, result.settings);
结果对象 result 的 shape 和输入完全一致——输入是 {user, posts, settings},输出也是这三个 key,没有顺序依赖,代码可以自由重组。
配合解构赋值更优雅:
const { user, posts, settings } = await Promise.allKeyed({
user: fetch('/api/user').then(r => r.json()),
posts: fetch('/api/posts').then(r => r.json()),
settings: fetch('/api/settings').then(r => r.json()),
});
renderDashboard(user, posts, settings);
这和以前看起来差不多,但关键区别在于:user、posts、settings 现在是对象的 key,编译器会强制校验 key 是否存在,漏写或拼错都会报错,而不是默默拿到 undefined。
Promise.allSettledKeyed() 的区别
Promise.allKeyed() 和原版 Promise.all 一样,任一 promise reject 就整体 reject,适合「全部必须成功」的场景。
Promise.allSettledKeyed() 则像 Promise.allSettled 一样,无论每个 promise 是成功还是失败都返回,最终结果是一个对象,每个 key 对应 {status: 'fulfilled', value: ...} 或 {status: 'rejected', reason: ...}。它适合「每个请求独立,部分失败不影响其他结果展示」的场景:
const results = await Promise.allSettledKeyed({
user: fetch('/api/user').then(r => r.json()),
posts: fetch('/api/posts').then(r => r.json()),
analytics: fetch('/api/analytics').then(r => r.json()),
});
if (results.user.status === 'fulfilled') {
renderUser(results.user.value);
}
if (results.analytics.status === 'rejected') {
// analytics 失败不影响页面渲染,只记录日志
console.error('analytics 加载失败:', results.analytics.reason);
}
什么时候该用
团队协作时,如果多人负责不同的 API 接入,对象式的 API 天然比数组更抗重构。一个新来的同事在并发请求里加了一个字段,只需要在自己的对象里加一行,不需要动别人的代码,也不用担心破坏了解构赋值的顺序。
这是 ES2027 规范里的新成员,目前 Firefox 155 首发,Chrome 正在跟进。其他浏览器暂时可以用 core-js 的 polyfill 先行接入。
下一步:在你的仪表盘或管理后台里扫一遍 Promise.all([...]),如果发现解构赋值的变量名和数组里的顺序有对应关系——这个模式今天可以换了。
参考来源:MDN Web Docs(Promise.allKeyed 规范文档)、AWNews(Firefox 155 发布摘要)、Microsoft Learn(Promise 对象文档)
评论区
登录后可评论。