Promise.prototype.finally 是释放资源锁的理想工具,因其在 Promise settled 后自动执行、不依赖状态、不影响链式值传递,避免了 then/catch 重复编写导致的遗漏或错误。

Promise.prototype.finally 是处理“无论成功或失败都必须执行”的清理逻辑的理想工具,特别适合释放局部资源锁(如内存缓存锁、临时文件句柄、UI 加载态、数据库连接池中的租用标记等)。它不接收 Promise 的结果值,也不影响链式传递的值,语义清晰、行为稳定。
为什么 finally 比 then/catch 更适合释放锁
常见误区是用 then().catch() 重复写释放逻辑,容易遗漏或写错;而 finally 自动在 Promise settled(fulfilled 或 rejected)后触发,且不关心状态,避免逻辑重复和遗漏:
- 不会因上一个
then抛出新错误而跳过(finally仍会执行) - 不改变 Promise 链后续接收到的值或错误,保持数据流干净
- 代码更短、意图更明确:「锁必须在这里解」一眼可见
典型场景:临时加锁 + finally 释放
例如,在发起请求前加 UI 锁(禁用按钮),无论请求成功或报错,都要解锁:
let isLocked = false;
function apiCall() {
if (isLocked) return Promise.reject(new Error('locked'));
isLocked = true;
return fetch('/data')
.then(res => res.json())
.catch(err => { throw new Error(`API failed: ${err.message}`); })
.finally(() => {
isLocked = false; // ✅ 保证执行
console.log('lock released');
});
}
配合 async/await 使用时的注意事项
在 async 函数中,finally 不能直接附加在 await 表达式后(因为 await 不返回 Promise 实例),需显式调用 .finally():
- ✅ 正确写法:将异步操作包装为 Promise 调用
.finally() - ❌ 错误写法:
await promise.finally(...)—— 语法合法但逻辑错位(await 等待的是 finally 返回的新 Promise,不是原逻辑)
推荐模式:
async function doWork() {
const lock = acquireLock();
try {
const data = await fetch('/api').then(r => r.json());
return process(data);
} catch (err) {
throw err;
} finally {
releaseLock(lock); // ✅ 在 try/catch/finally 中释放,语义等价于 Promise.finally
}
}
注意:这里用 try...finally 是等效替代,适用于已用 async/await 的上下文;若整个流程基于 Promise 链,则优先用 .finally() 保持风格统一。
进阶:封装可复用的「带锁 Promise」工厂
把加锁、执行、释放抽象成高阶函数,避免重复样板:
function withLock(promiseFactory, releaseLock) {
const lock = acquireLock();
return promiseFactory()
.finally(() => releaseLock(lock));
}
// 使用
withLock(
() => fetch('/data').then(r => r.json()),
(lock) => { console.log('released', lock); }
).then(data => console.log(data));
该模式支持任意异步操作,锁的获取与释放完全解耦,便于测试和替换(如换成 Redis 分布式锁客户端)。










