从 Promise.all 到 Promise.allSettled:一次接口优化的真实经历
有一次,我需要同时调用多个接口来获取数据。为了提高效率,我使用了 Promise.all 方法。代码部署后,一切看起来都很完美,我觉得自己解决了一个大问题。
但很快,问题出现了。当其中一个接口偶尔失败时,整个页面都会崩溃。用户看到的是一个错误提示,而不是他们需要的数据。这让我意识到,我需要找到一个更好的解决方案。
发现问题
在我的项目中,有一个数据面板需要从多个服务获取信息:
const [user, orders, notifications] = await Promise.all([
getUser(),
getOrders(),
getNotifications(),
]);在大多数情况下,这段代码运行得很好。但是,只要其中任何一个请求失败,整个 Promise.all 就会立即停止,并抛出错误。这意味着即使其他请求都成功了,用户也看不到任何数据。
找到解决方案
经过研究,我发现了 Promise.allSettled 这个方法。它与 Promise.all 的不同之处在于,它会等待所有请求都完成,不管它们是成功还是失败。
const results = await Promise.allSettled([
getUser(),
getOrders(),
getNotifications(),
]);
// 处理结果
results.forEach(result => {
if (result.status === 'fulfilled') {
// 请求成功
console.log('成功获取数据:', result.value);
} else {
// 请求失败
console.log('获取数据失败:', result.reason);
}
});这种方法的好处
使用 Promise.allSettled 后,我获得了几个重要的优势:
显示部分数据:即使某些请求失败,成功的数据仍然可以显示给用户
明确错误位置:可以清楚地知道是哪个模块出了问题
针对性重试:只重新请求失败的接口,而不是全部重试
实际应用建议
在实际项目中,你可以这样处理:
async function loadDashboardData() {
const results = await Promise.allSettled([
getUserInfo(),
getOrderList(),
getNotificationCount(),
getRecommendations()
]);
const data = {};
const errors = [];
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
// 根据索引确定是哪个请求的数据
switch(index) {
case 0: data.user = result.value; break;
case 1: data.orders = result.value; break;
case 2: data.notifications = result.value; break;
case 3: data.recommendations = result.value; break;
}
} else {
errors.push({index, error: result.reason});
}
});
return {data, errors};
}选择性重试的实现
对于失败的请求,你可以选择性地重试:
async function loadDataWithRetry() {
let results = await Promise.allSettled(requests);
// 找出失败的请求
const failedRequests = results
.map((result, index) => ({result, index}))
.filter(item => item.result.status === 'rejected')
.map(item => requests[item.index]);
// 只重试失败的请求
if (failedRequests.length > 0) {
const retryResults = await Promise.allSettled(failedRequests);
// 合并重试结果...
}
return results;
}什么时候用哪个方法
根据我的经验,这两种方法各有适用场景:
使用 Promise.all 的情况:
用户登录验证(所有步骤都必须成功)
订单支付流程(每个环节都不能出错)
数据提交(所有操作都要完成)
使用 Promise.allSettled 的情况:
数据面板显示(部分数据可用就好)
侧边栏小工具(某些内容可以缺失)
推荐内容加载(没有推荐也能正常使用)
最佳实践
区分重要程度:把关键数据和次要数据分开处理
提供备用方案:对于失败的内容,显示占位符或默认内容
用户提示:告诉用户哪些内容暂时不可用
错误记录:记录失败的原因,便于后续优化
代码示例:混合使用
async function loadPageData() {
// 关键数据 - 必须成功
const [user, mainContent] = await Promise.all([
getUserData(),
getMainContent()
]);
// 次要数据 - 可有可无
const secondaryResults = await Promise.allSettled([
getRecommendations(),
getSidebarWidgets(),
getAdvertisement()
]);
return {
user,
mainContent,
secondaryData: processSecondaryResults(secondaryResults)
};
}总结
从 Promise.all 切换到 Promise.allSettled,虽然只是一个小小的改变,但却显著提升了应用的稳定性。用户不再因为某个非关键接口的失败而看到空白页面,而是能够继续使用其他正常的功能。
在实际开发中,理解不同工具的特点,并在合适的场景使用它们,这是提升代码质量的重要一步。如果你的项目中也存在类似的情况,不妨试试 Promise.allSettled,它可能会帮你避免很多意想不到的问题。
记住,好的代码不仅要考虑正常情况,还要为可能出现的异常情况做好准备。这样才能打造出真正稳定可靠的应用。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!