一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

如何用 Promise.prototype.finally 统一处理不管请求成功还是失败都要隐藏的骨架屏

时间:2026-07-19 11:06:49 编辑:袖梨 来源:一聚教程网

Promise.prototype.finally 适合隐藏骨架屏,因为它在 Promise settled 后必执行,不干扰返回值且不吞错误;需置于链末端或 try/finally 块中,避免竞态与错误覆盖。

Promise.prototype.finally 为什么适合隐藏骨架屏

因为 finally 不关心上一个 Promise 是 fulfilled 还是 rejected,只要状态 settled 就执行,天然契合“请求结束就收起骨架屏”的语义。它不接收参数,不会干扰链式返回值,也不会意外吞掉错误——这点比在 thencatch 里重复写隐藏逻辑更安全。

在 fetch 请求中正确使用 finally 隐藏骨架屏

常见错误是把 finally 放错位置,比如写在 catch 后面却忘了 then 也可能抛错(例如 response.json() 解析失败),导致骨架屏卡住。正确做法是让 finally 紧跟在最终的 Promise 链末端:

showSkeleton();fetch('/api/data')  .then(res => {    if (!res.ok) throw new Error(`HTTP ${res.status}`);    return res.json();  })  .then(data => render(data))  .catch(err => handleError(err))  .finally(() => hideSkeleton());

注意:如果中间用了 async/awaitfinally 不能直接链在 await 表达式后,得包一层 Promise:

showSkeleton();try {  const res = await fetch('/api/data');  const data = await res.json();  render(data);} catch (err) {  handleError(err);} finally {  hideSkeleton();}

finally 中修改状态可能引发竞态问题

当多个并发请求共用同一个骨架屏开关(比如全局 isLoading 变量),直接在每个请求的 finally 里设 isLoading = false 会导致后完成的请求覆盖先完成的,骨架屏提前消失。解决方法:

  • 用计数器:发起请求前 pendingCount++finallypendingCount--,仅当 pendingCount === 0 时隐藏
  • 用 AbortController 配合状态标记,或改用 React Query / SWR 等自带 loading 状态管理的库
  • 避免共享状态,每个组件维护自己的 isPending,靠 UI 层统一控制显示逻辑

finally 不会阻止错误向上传播,但容易误以为它会

这是最常被忽略的一点:finally 里的代码执行完后,原始 rejection 仍会继续冒泡。如果你在 finally 里写了 throw 或返回 rejected Promise,它会覆盖原错误;但什么也不做,就不会影响错误流向。所以以下写法是安全的:

fetch('/api/data')  .then(handleSuccess)  .catch(handleError)  .finally(() => hideSkeleton()); // ✅ 原错误仍能被外层 try/catch 捕获

而这样会出问题:

.finally(() => {  hideSkeleton();  return Promise.reject(new Error('finally broke it')); // ❌ 覆盖了原始错误})

实际项目中,只要 finally 体里只做副作用(如 DOM 更新、日志、清理定时器),就不需要担心错误流被破坏。

热门栏目