前端 Web Locks 怎样查询当前持有者
来源:17golang原创
时间:2026-10-20 03:06:46 284浏览 收藏
多个标签页同时同步数据时,最难排查的往往不是“有没有调用 navigator.locks.request()”,而是当前到底谁拿到了锁、哪些请求还在排队。Web Locks 提供了 navigator.locks.query(),它返回当前 origin 的诊断快照:held 是已持有的锁,pending 是等待中的请求。它只读状态,不会抢锁、释放锁或取消等待。
官方地址:https://developer.mozilla.org/en-US/docs/Web/API/LockManager/query
排查“同步任务卡住”时,先看held里对应的name、mode和clientId;再看pending是否有同名请求。不要把 pending 中的上下文当成当前持有者。
先确认触发信号与查询边界
如果页面表现为重复同步、某个任务迟迟没有进入回调,或者多个标签页像是在互相等待,可以先把问题收窄到“锁状态快照”。Web Locks 按 origin 隔离,来自不同协议、域名或端口的页面不会共享同一个锁管理器。
查询依赖安全上下文,并且页面需要能够取得 navigator.locks。因此排障记录里要同时写下页面 origin、是否 HTTPS、运行环境是标签页还是 Worker。下面的代码只做能力判断,不会创建锁:
function canInspectLocks() {
// Web Locks 需要安全上下文,先避免在不支持的环境中直接访问。
return window.isSecureContext && "locks" in navigator;
}
if (!canInspectLocks()) {
console.warn("当前环境无法读取 Web Locks 状态");
}
这里的“无法读取”可能来自不安全上下文,也可能来自浏览器或运行环境没有实现 API。它不是业务锁被占用的证据。
读取 held 与 pending 两组锁信息
query() 不接收参数,返回一个 Promise。快照中的每个锁记录通常包含三个对排障最有用的字段:name 是请求时的锁名,mode 是 exclusive 或 shared,clientId 是发起请求的执行上下文标识。

async function printLockSnapshot() {
// query() 只读当前 origin 的锁管理器快照,不会修改锁状态。
const snapshot = await navigator.locks.query();
// held 是当前已经授予的锁;同名 shared 锁可能同时出现多个记录。
for (const lock of snapshot.held) {
console.log("held", {
name: lock.name,
mode: lock.mode,
clientId: lock.clientId
});
}
// pending 是尚未授予的请求,不能当作当前持有者。
for (const request of snapshot.pending) {
console.log("pending", {
name: request.name,
mode: request.mode,
clientId: request.clientId
});
}
return snapshot;
}
为了让值班记录可读,可以把输出按“持有区”和“等待区”分开保存。没有任何锁时,两个数组都可能为空;这只能说明查询时刻没有可见的持有或等待记录,不代表稍后不会有新请求。
根据 clientId 判断当前持有者
判断“谁持有锁”时,先在 held 中按 name 过滤,再记录同一条记录的 mode 与 clientId。pending 只说明有请求排队,它的 clientId 可以帮助定位等待方,但不能证明该上下文已经执行了临界区代码。

function summarizeLock(snapshot, targetName) {
// 只从 held 中寻找真正已授予的目标锁,避免把等待请求算成持有者。
const holders = snapshot.held
.filter((lock) => lock.name === targetName)
.map((lock) => ({
mode: lock.mode,
clientId: lock.clientId
}));
// pending 用于解释“为什么还有请求没进去”,单独保留等待上下文。
const waiters = snapshot.pending
.filter((lock) => lock.name === targetName)
.map((lock) => ({
mode: lock.mode,
clientId: lock.clientId
}));
return { targetName, holders, waiters };
}
这里的 clientId 是浏览器为执行上下文提供的标识,不应直接当作用户 ID、标签页标题或业务会话名。若需要把它映射到业务日志,应在应用自己的请求上下文中另行记录,不要猜测 Web Locks 字段的含义。
处理查询失败、空快照与回滚路径
查询本身可能返回拒绝的 Promise。官方接口列出的常见异常包括文档尚未 fully active 时的 InvalidStateError,以及无法取得锁管理器时的 SecurityError。运行手册中应把这些情况和“锁确实被别的标签页持有”分开。
async function inspectLocksSafely() {
try {
// 只在能力检查通过后调用 query(),将异常转成可追踪的诊断结果。
const snapshot = await navigator.locks.query();
return {
ok: true,
held: snapshot.held,
pending: snapshot.pending
};
} catch (error) {
// 保留异常类型,便于区分上下文问题与业务锁等待。
return {
ok: false,
errorName: error.name,
message: error.message
};
}
}
如果结果是空快照,复盘时记录查询时间和 origin;如果结果是异常,记录 errorName 和页面生命周期状态。回滚路径也很明确:查询函数只撤回诊断日志,不对现有锁做强制操作;真正的锁释放仍由持锁请求的异步回调结束来完成。
告警确认与复盘项
线上告警可以把“同名锁持续出现在 held”“pending 数量持续增长”“query() 连续失败”作为不同信号,但不要只凭一次快照下结论。对每次采样保存 name、mode、clientId、origin 和采样时间,之后再和同步任务日志关联。
复盘时重点问三件事:锁名是否过于宽泛,导致无关任务排队;持锁回调是否包含了不必要的慢操作;等待方是否有超时或用户可见的降级路径。query() 能告诉你“当前看见了什么”,但不能替应用决定锁应该由谁释放。
常见问题
query() 能直接查某一个锁名吗?
不能。query() 没有参数,会返回快照;需要查询某个名字时,在返回的 held 和 pending 数组上用 name 过滤。
pending 里的 clientId 是当前持有者吗?
不是。它代表发起等待请求的执行上下文。当前持有者应从同名锁的 held 记录中判断。
调用 query() 会让等待请求立即获得锁吗?
不会。它是诊断查询,不改变授予、排队或释放状态。
把 held 当作当前授予结果,把 pending 当作等待记录,再用 clientId 关联执行上下文,就能把“谁在持锁”从猜测变成可复查的状态快照。
-
459 收藏
-
264 收藏
-
285 收藏
-
140 收藏
-
470 收藏
-
432 收藏
-
432 收藏
-
464 收藏
-
379 收藏
-
295 收藏
-
182 收藏
-
150 收藏
-
204 收藏
-
114 收藏
-
306 收藏
-
184 收藏
-
404 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习