promise.prototype.finally 专用于无条件执行资源清理,如关 loading、解锁按钮、清定时器等,不依赖结果且不改变状态;需置于链末端或 try/catch 的 finally 块中,避免在其中读取数据、判断错误或发起异步请求。

用 Promise.prototype.finally 执行资源清理,核心是抓住它“无条件执行、不依赖结果、不改变状态”这三个特性。它不是用来处理业务逻辑的,而是专为收尾动作设计的——比如关 loading、解锁按钮、释放锁、清定时器、还原 UI 状态。
适合 finally 的典型清理动作
这些操作共同点是:不需要知道请求成功还是失败,也不需要原始响应或错误对象。
- 设置
loading = false或隐藏加载蒙层 - 将提交按钮从
disabled恢复为可用 - 重置表单的
isSubmitting标志位 - 清除临时缓存标记或内存引用(如
cacheMap.delete(key)) - 调用
AbortController.abort()后的善后(注意:abort 本身会 reject,finally 仍会触发)
必须避开的常见误用
finally 回调里写这些,大概率出错或逻辑错位:
-
读取响应数据:比如
console.log(data)——data在finally里是未定义的 -
判断错误类型并弹提示:比如
if (err?.code === 'NETWORK')——err不传入finally -
发起异步清理请求:比如
return fetch('/log')—— 这个 Promise 不会被等待,主链照常往下走 -
在链中间挂 finally:比如
.then(...).finally(...).catch(...)—— 它只守then阶段,fetch失败时不会触发
正确挂载位置:守整个流程
要确保 finally 覆盖请求发起、解析、错误处理全过程,必须把它放在链的最末端,且前面没有被 catch 截断:
fetch('/api/user')
.then(res => {
if (!res.ok) throw new Error('HTTP error');
return res.json();
})
.catch(err => {
console.error('Request failed:', err);
// 这里做错误专属处理(如上报、toast)
})
.finally(() => {
setLoading(false); // ✅ 此处一定执行
button.disabled = false;
});
等价于把 finally 直接连在原始 Promise 实例之后,它才真正“守得住”整个异步路径。
配合 async/await 的写法要点
await 表达式本身不返回 Promise 实例,不能直接链式调用 .finally()。正确做法是显式调用:
async function loadData() {
setLoading(true);
try {
const res = await fetch('/api');
const data = await res.json();
return data;
} catch (err) {
throw err;
} finally {
// ✅ 原生 try/catch/finally 更直观,推荐用于复杂流程
setLoading(false);
}
}
// 或者包装 Promise:
const promise = fetch('/api').then(r => r.json());
promise.finally(() => setLoading(false)); // ✅ 可行
注意:不要写 await promise.finally(...),这会等待 finally 返回的新 Promise,而非原始逻辑。











