当前位置:首页 > 文章列表 > 文章 > 前端 > 前端 Web Locks 怎样查询当前持有者

前端 Web Locks 怎样查询当前持有者

来源:17golang原创 2026-10-20 03:06:46 0浏览 收藏

多个标签页同时同步数据时,最难排查的往往不是“有没有调用 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 是发起请求的执行上下文标识。

Web Locks query 返回 held 与 pending 快照的结构说明图
图1:Web Locks 查询快照结构,held 表示已持有锁,pending 表示等待中的请求。
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 可以帮助定位等待方,但不能证明该上下文已经执行了临界区代码。

标签页和 Worker 通过 Web Locks 形成 held 与 pending 诊断边界的结构图
图2:锁持有者诊断边界,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 关联执行上下文,就能把“谁在持锁”从猜测变成可复查的状态快照。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
前端 Intl.Segmenter 怎样按语言边界切分文本前端 Intl.Segmenter 怎样按语言边界切分文本
上一篇
前端 Intl.Segmenter 怎样按语言边界切分文本
农业经营者怎样留存农产品批次追溯记录
下一篇
农业经营者怎样留存农产品批次追溯记录
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    553次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    604次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    631次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    586次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    410次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码