promise 的 finally 方法提供确定性清理机制,无论 promise 成功或失败均执行回调,不接收参数、不改变状态、不中断链式传递,确保 ui 重置、资源释放等操作可靠执行。
promise 的执行环境清理机制,核心在于 finally 方法提供的确定性回调能力——它不关心 promise 最终是成功还是失败,只确保一段逻辑在异步操作生命周期结束时被执行。
finally 是清理操作的“守门人”
它不像 then 或 catch 那样依赖状态分支,而是直接挂载在 Promise 链末端,无论上游走哪条路径(resolve 还是 reject),都会触发。这种设计模拟了传统 try-catch-finally 中的 finally 块语义。
- 回调函数不接收参数,无法判断结果状态,天然避免误判逻辑
- 内部实现会等待原 Promise 完成后,同步执行回调,再原样传递结果或错误
- 如果 finally 回调本身抛错,该错误不会中断原 Promise 的链式传递,而是作为独立异常被抛出
常见清理场景与写法
清理不是泛泛而谈,而是针对具体资源或状态变化的操作:
- UI 状态重置:隐藏加载遮罩、禁用提交按钮、清空表单 loading 状态
- 连接/句柄释放:关闭 WebSocket 连接、取消 fetch 请求的 AbortController、释放 Canvas 上下文引用
-
内存管理辅助:清除大型临时对象引用(如
dataURL、Blob),帮助 GC 更早回收
示例:
fetch('/api/data')
.then(res => res.json())
.catch(err => console.error('请求失败', err))
.finally(() => {
document.getElementById('loading').style.display = 'none';
// 不管成功失败,都关闭加载态
});
为什么不用 then + catch 组合替代?
表面上看,把清理逻辑写在 then 和 catch 的后续 then 里也能达到类似效果,但存在隐患:
- 清理逻辑被包裹在 Promise 链中,若上游
then抛错且未被捕获,清理可能跳过 - 代码意图模糊:多个
then堆叠会让“清理”意图被淹没,可读性下降 - finally 的回调不改变 Promise 状态,不会意外吞掉错误或篡改返回值
清理时机由微任务队列保障
Promise 的整个生命周期(包括 finally 回调)运行在微任务(microtask)队列中。这意味着:
- 清理逻辑一定在当前宏任务(如点击事件、定时器回调)结束后、下一个宏任务开始前执行
- 它比
setTimeout更快,能及时响应状态变化,避免 UI 卡顿或状态残留 - 多个连续的 finally 调用会按顺序排队执行,不会并发冲突











