finally()是隐藏Loading的最佳选择,因其无论Promise成败均执行且不接收前序值,语义契合“统一关loading”,比重复写then/catch或try/catch更简洁;需防闭包陷阱、并发覆盖及执行顺序误解,并注意IE等旧浏览器兼容性。

为什么 finally() 是隐藏 Loading 的最佳选择
因为 finally() 无论 Promise 被 resolve 还是 reject 都会执行,且不接收前序状态值,天然适配“不管成功失败都要关 loading”的语义。它比在 then() 和 catch() 里各写一遍更简洁,也比用 try/catch/finally 包裹 await 更符合链式请求场景。
finally() 里不能直接修改 loading 状态?小心闭包陷阱
常见错误是把 loading 变量(比如 loadingRef 或 setLoading)写在请求函数外部,但实际调用时多个并发请求共享同一个引用,导致后发起的请求覆盖前一个的 loading 控制逻辑。
- 推荐做法:每个请求维护独立的 loading 标识,例如用请求 URL 或唯一 key 作为键名存入 Map 或对象
- 若用 React,优先用局部
useState或useRef绑定到当前组件实例,避免跨组件污染 - 不要在
finally()回调里直接调用全局setLoading(false),除非你确认此刻没有其他请求在进行
和 then().catch().finally() 链混用时的执行顺序陷阱
很多人以为 finally() 总是最后执行,其实它只保证在当前 Promise 结束后立即执行,但不阻塞后续链的构造。如果在 finally() 后还接了 then(),那个 then() 仍会等 finally() 返回的 Promise 完成 —— 而 finally() 默认返回上一个 Promise 的结果(或 rejection 原因),不是新 Promise。
- 如果你在
finally()里做了异步操作(比如日志上报),必须显式返回Promise,否则后续then()会立即执行,可能拿到未完成的副作用结果 - 正确写法:
finally(() => new Promise(resolve => { hideLoading(); resolve(); })) - 更稳妥的做法是:把 loading 控制逻辑抽离为纯同步函数,
finally(hideLoading)即可
兼容性与降级方案:IE 和旧版 Safari 不支持 finally()
IE 完全不支持,iOS Safari undefined is not a function。
- 检查运行时支持:
typeof Promise.prototype.finally === 'function' - 简单降级:用
.then(hideLoading, hideLoading)替代,注意两个回调都需调用同一函数 - 构建时处理:Webpack/Babel 用户可通过
@babel/preset-env自动注入 polyfill,但要确保core-js/stable/promise已引入 - 不建议手动重写
finally方法,容易破坏 Promise 规范中的错误传播行为
finally(hideLoading),而是让 hideLoading 知道“该关哪一个 loading”,以及确保它不会被并发请求互相干扰。状态归属和作用域边界,比语法本身更关键。

















