LockManager 和 Lock 不支持 structuredClone(),因为它们是非可序列化、不可克隆的宿主对象,调用会抛出 DataCloneError;应按需重新申请锁,而非拷贝锁对象。

不能用 structuredClone() 拷贝 LockManager 或锁对象(如 Lock)。这是由浏览器规范和底层实现决定的——LockManager 和 Lock 都是非可序列化、不可克隆的宿主对象,调用 structuredClone() 会直接抛出 DataCloneError。
为什么 LockManager 和 Lock 不支持 structuredClone()
LockManager 是一个全局接口实例(通常通过 navigator.locks 获取),代表浏览器级的资源锁协调机制;Lock 是异步获取锁后返回的临时句柄,仅在当前锁持有期间有效,且与底层锁状态强绑定。它们既没有可序列化的内部状态,也不符合 HTML 标准中可转移/可克隆对象 的定义。
尝试克隆会得到明确错误:
const lock = await navigator.locks.request('my-res', () => {});
structuredClone(lock); // ❌ TypeError: DataCloneError: Cannot clone object of type 'Lock'
structuredClone(navigator.locks); // ❌ Same error
替代方案:按需重新申请锁,而非拷贝锁
锁的本质是“排他性访问权”,不是可传递的数据。正确做法是让目标上下文(如新 Worker、新 Promise 链、新函数作用域)**独立发起锁请求**,而不是试图复用旧锁引用。
- 在 Worker 中使用锁:主线程传入锁名和选项,Worker 自己调用
navigator.locks.request() - 跨函数传递锁逻辑:封装成可复用的 async 函数,每次调用都新建锁请求
- 避免“共享锁实例”思维:锁没有身份标识,只有名称(name)+ 作用域(scope)+ 持有者(当前执行上下文)共同决定其行为
需要持久化锁状态?用外部协调机制
如果业务上需要“记住某资源已被锁定”,不要依赖锁对象本身,而应结合其他可序列化的状态:
- 用 IndexedDB 或 localStorage 记录锁名、持有者 ID、时间戳、预期释放条件
- 配合定时轮询或 BroadcastChannel 通知其他上下文锁状态变更
- 在 request 时设置
ifAvailable: true或steal: true实现更灵活的抢占策略
例如:
// 请求前先查本地记录(非锁对象,而是业务状态)
const lockState = await db.get('lock:api-cache');
if (!lockState || Date.now() - lockState.acquiredAt > 30_000) {
await navigator.locks.request('api-cache', async () => {
await db.put('lock:api-cache', {
acquiredAt: Date.now(),
owner: self.id
});
// 执行受保护操作...
});
}
小结:锁不可复制,但锁逻辑可重用
structuredClone() 的设计目标是深拷贝纯数据(Object、Array、Map、Set、TypedArray 等)和部分可转移对象(MessagePort、ArrayBuffer)。LockManager 和 Lock 明确被排除在外。不要尝试绕过限制(比如 JSON.stringify 再 parse),那只会丢失全部语义。专注在“何时/何处申请锁”和“如何协同锁生命周期”上,才是实际可行的路径。











