Promise本身不提供锁机制,但可通过共享pendingPromise变量实现异步锁:首次调用执行异步操作并保存Promise,后续调用直接等待该Promise,从而避免并发重复执行。

Promise 本身不提供锁机制,但可以用它配合状态管理来模拟“异步锁”,核心思路是:**用一个 Promise 变量保存当前正在执行的异步操作,后续请求等待它完成,而不是各自发起新操作**。这能有效避免对同一资源的并发写入或重复初始化等问题。
用 pendingPromise 实现串行化调用
最常用且轻量的方式是维护一个 共享的 pendingPromise 变量,每次请求都复用(或链式延续)这个 Promise:
- 首次调用时,执行异步操作,并把返回的 Promise 赋给 pendingPromise
- 后续调用直接返回 pendingPromise(不新建请求),等它 settle 后再 resolve 结果
- 操作完成后,可选择重置 pendingPromise(如需支持下一轮锁)或保持为 null
示例(防重复登录态刷新):
let pendingRefresh = null;function refreshToken() {
if (pendingRefresh) {
return pendingRefresh;
}
pendingRefresh = fetch('/refresh-token')
.then(r => r.json())
.finally(() => { pendingRefresh = null; });
return pendingRefresh;
}
封装成可复用的 AsyncLock 类
如果多个地方需要类似逻辑,可以封装一个简单锁类,内部管理 resolve/reject 控制权:
立即学习“Java免费学习笔记(深入)”;
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- lock() 返回一个 Promise,只有拿到“锁”后才执行传入的 fn
- 内部用 isLocked 标记 + queue 队列暂存等待者(可选,实现 FIFO)
- fn 执行完毕自动释放锁,并触发下一个等待者
简易版(无队列,只允一个等待者):
class AsyncLock {constructor() {
this.isLocked = false;
this.queue = [];
}
async lock(fn) {
if (!this.isLocked) {
this.isLocked = true;
try {
return await fn();
} finally {
this.isLocked = false;
if (this.queue.length > 0) {
this.queue.shift()(); // 触发下一个
}
}
} else {
return new Promise(resolve => {
this.queue.push(() => resolve(this.lock(fn)));
});
}
}
}
注意边界与常见陷阱
这类手动锁容易忽略的关键点:
- 错误未捕获会导致锁永久挂起:务必在 finally 或 catch 中释放锁,否则后续调用永远等待
- 不要在 lock 内部再调用 lock(除非设计为可重入),否则可能死锁
- Promise 缓存 ≠ 锁:单纯 cache Promise(如 memoize)只防重复执行,但不阻塞并发调用;而锁要确保“任一时刻最多一个执行中”
- 若需超时控制,可在 lock 方法里包装
Promis.race([lock(), timeout()])
更现代的选择:使用 async-mutex 库
生产环境推荐用成熟库如 async-mutex,它已处理好重入、取消、超时、公平队列等细节:
import { Mutex } from 'async-mutex';const mutex = new Mutex();
async function criticalSection() {
const release = await mutex.acquire();
try {
// 执行敏感操作(如写 localStorage、调用支付接口)
} finally {
release();
}
}
不复杂但容易忽略的是:锁的本质不是 Promise,而是对执行时机的协调。Promise 只是帮你优雅地表达“等别人做完我再做”。

















