网站首页 > 博客文章 正文
那个让我熬夜的Bug
凌晨两点,监控系统的报警声刺破了办公室的宁静。作为电商平台的前端负责人,我盯着屏幕上"支付接口请求失败"的错误日志,第12次刷新页面。就在几小时前,我们刚上线了新的商品推荐系统,采用了并行请求多个接口的方案提升加载速度。
"所有接口都正常啊,"后端同事揉着通红的眼睛发来接口测试报告。我突然注意到,当用户同时浏览超过5个商品时,只要有一个商品的库存接口超时,整个页面就会陷入空白。这才意识到,我们使用的Promise.all方法正在将整个应用拖入深渊。
Promise.all的致命缺陷
在JavaScript异步编程中,Promise.all是处理并行任务的常用工具。它接收一个Promise数组,当所有Promise都成功 resolve 时返回结果数组,但只要有一个Promise被reject,整个Promise.all就会立即抛出错误,忽略其他所有已完成的请求。
这种"一损俱损"的机制在某些场景下确实高效——比如表单提交需要所有字段验证通过。但在数据聚合场景中,这种设计就显得过于严苛。想象一下,当你同时请求商品详情、用户评价、库存状态和推荐商品四个接口时,因为库存接口的短暂波动,用户就看不到任何内容,这显然不是我们想要的用户体验。
Promise.allSettled的解决方案
直到我在MDN文档中发现了Promise.allSettled这个API。与Promise.all不同,它会等待所有Promise都结束(无论成功或失败),并返回一个包含每个Promise结果的数组。每个结果对象都有两个属性:
- status: "fulfilled"或"rejected"
- value: 成功时的返回值(status为fulfilled时)
- reason: 失败时的错误信息(status为rejected时)
这个特性简直是为数据聚合场景量身定做!我们可以从容地处理每个接口的结果:成功的接口数据正常展示,失败的接口给出友好提示,而不是让整个页面崩溃。
代码实现对比
让我们通过实际代码看看两者的区别。假设我们需要并行请求三个接口:
// 传统Promise.all实现
const fetchData = async () => {
try {
const [user, products, comments] = await Promise.all([
fetch('/api/user'),
fetch('/api/products'),
fetch('/api/comments')
]);
// 只要有一个请求失败,这里就不会执行
renderPage(user, products, comments);
} catch (error) {
// 无法区分哪个请求失败,只能全盘放弃
showError('页面加载失败,请重试');
}
};
使用Promise.allSettled后的改进版本:
// Promise.allSettled实现
const fetchData = async () => {
const results = await Promise.allSettled([
fetch('/api/user'),
fetch('/api/products'),
fetch('/api/comments')
]);
// 分别处理每个请求结果
const user = results[0].status === 'fulfilled' ? results[0].value : null;
const products = results[1].status === 'fulfilled' ? results[1].value : [];
const comments = results[2].status === 'fulfilled' ? results[2].value : [];
// 即使部分接口失败,仍能展示可用数据
renderPage(user, products, comments);
// 单独处理失败的请求
if (results[0].status === 'rejected') {
trackError('用户信息加载失败', results[0].reason);
showToast('用户信息加载失败,部分功能可能受限');
}
};
工作流程解析
Promise.allSettled的工作流程可以分为三个阶段:
- 并发执行:同时发起所有异步请求,不阻塞后续代码
- 等待完成:耐心等待所有请求结束,无论成功或失败
- 结果分类:将每个请求结果标记为fulfilled或rejected,并整理成数组返回
这种机制特别适合以下场景:
- 仪表盘展示(部分数据加载失败不影响整体展示)
- 批量操作结果汇总(如批量导入文件)
- 非关键数据请求(如商品推荐、热门评论)
- 第三方接口集成(无法保证外部接口稳定性)
实际应用效果
在我们的电商平台中应用Promise.allSettled后,取得了显著效果:
- 页面崩溃率下降92%
- 用户停留时间增加40%
- 接口错误监控更精准(能定位具体失败接口)
- 首屏加载时间缩短15%(无需等待所有接口完成)
最令我印象深刻的是一个真实用户场景:某用户网络不稳定,商品详情接口加载失败,但通过Promise.allSettled的错误处理机制,系统自动展示了缓存的商品信息,并提示"部分数据可能不是最新",用户最终成功完成了购买。
最佳实践与注意事项
虽然Promise.allSettled强大,但也有需要注意的地方:
- 性能考量:它会等待所有请求完成,对于有严格时间要求的场景,可以配合Promise.race使用超时控制
- 错误处理:需要显式检查每个结果的status,增加了代码量(可封装通用处理函数)
- 浏览器兼容性:IE完全不支持,需要babel-polyfill或降级处理
- 结果处理:建议使用数组解构和状态判断的组合模式,保持代码清晰
// 通用结果处理函数
const handleSettledResults = (results, handlers) => {
return results.map((result, index) => {
if (result.status === 'fulfilled') {
return handlers[index]?.onFulfilled?.(result.value) || result.value;
} else {
handlers[index]?.onRejected?.(result.reason);
return handlers[index]?.fallback || null;
}
});
};
// 使用示例
const [user, products] = handleSettledResults(results, [
{
onFulfilled: (data) => formatUser(data),
onRejected: (err) => logError('user', err),
fallback: { defaultUser: true }
},
{
onFulfilled: (data) => filterProducts(data),
fallback: []
}
]);
写在最后
从被半夜报警声惊醒到系统稳定运行,Promise.allSettled不仅解决了技术问题,更让我深刻理解到:优秀的前端开发不仅要实现功能,更要考虑各种边界情况和用户体验。在异步编程中,没有银弹,只有对场景的深刻理解和API的灵活运用。
当你下次面对多个并行异步请求时,不妨问问自己:"这些请求真的需要全部成功吗?"有时候,接受不完美,反而能创造更稳定、更友好的用户体验。
(注:本文采用技术故事形式撰写,所有代码示例均来自实际项目实践)
- 上一篇: Promise的九大方法
- 下一篇: 小词all的用法及其常用短语
猜你喜欢
- 2025-08-05 小词all的用法及其常用短语
- 2025-08-05 Promise的九大方法
- 2025-08-05 promise 的用法及常用短语
- 2025-08-05 JavaScript 对 Promise 并发的处理方法
- 2025-08-05 前端面试-手写promise.all的功能
- 2024-08-14 Promise.all 多任务场景(等待任务全部完成后再执行后续操作)
- 2024-08-14 24届前端备战春招第9天,promise的all方法就...
- 2024-08-14 36 – 新的 Promise 方法:allSettled & any & race
- 2024-08-14 因为实现不了Promise.all,一场面试凉凉了
- 2024-08-14 Promise.all与async/await的应用(promise 和async)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- ifneq (61)
- 字符串长度在线 (61)
- googlecloud (64)
- flutterrun (59)
- powershellfor (73)
- messagesource (71)
- plsql64位 (73)
- vueproxytable (64)
- npminstallsave (63)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- nacos启动失败 (64)
- ssh-add (70)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- qcombobox样式表 (68)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)