Web Locks API 的等待请求如何支持用户主动取消
来源:17golang原创
时间:2026-10-09 21:29:00 239浏览 收藏
Web Locks API 的等待请求可以主动取消,但取消点只在“锁还没有授予”这一段。做法是在 navigator.locks.request() 的 options 中传入 AbortController.signal,用户点击取消或等待超时时调用 abort(),再把 AbortError 当作正常的放弃等待处理。若回调已经开始,锁会保持到回调返回;这时要另行取消业务任务,不能指望 request 的 signal 强行打断临界区。
官方地址:https://developer.mozilla.org/en-US/docs/Web/API/LockManager/request
把 Web Locks 的控制拆成两层:request signal 负责“还没拿到锁时退出队列”,业务 AbortController 负责“拿到锁后停止具体工作”。
signal取消的是未获锁的排队请求,拒绝结果通常是AbortError。- 回调一旦开始,锁的释放由回调返回决定,不能用同一个 signal 伪造强制解锁。
- 用户取消、等待超时、业务 fetch 中断和
finally清理应分别建模。
AbortSignal 只取消排队中的请求
同一个 origin 下的多个标签页或 Worker 可能竞争同名锁。默认的 exclusive 锁在前一个回调完成前会让后续请求排队,因此“取消等待”本质上是从队列中撤下尚未授予的请求。MDN 对 signal 的定义也明确指出:控制器中止时,如果请求还没有获授,锁请求会被丢弃。
async function waitForSyncLock(signal) {
try {
return await navigator.locks.request(
"document-sync",
{ mode: "exclusive", signal },
async (lock) => {
// 只有拿到锁后才进入互斥工作区。
await flushPendingChanges();
return lock?.name;
},
);
} catch (error) {
// AbortError 表示放弃排队,不应当当成业务故障重试。
if (error instanceof DOMException && error.name === "AbortError") {
return "cancelled-before-grant";
}
throw error;
}
}

这里的返回值来自回调,真正的锁释放仍由异步回调结束决定。代码中不要把 AbortError 记录成“锁损坏”;它表示当前调用者选择不再等待。
用 request 的 signal 配合超时与用户取消
等待超时和用户点击取消可以共享一个“等待控制器”,但业务处理最好使用另一个控制器。这样即使锁在取消信号到达前恰好授予,回调也能根据业务信号停止网络或计算,并在 finally 中完成清理。
async function syncWithCancel(userSignal) {
const waitController = new AbortController();
const workController = new AbortController();
const timeoutId = setTimeout(() => waitController.abort(), 3000);
// 用户取消同时终止等待和已开始的业务任务。
userSignal.addEventListener("abort", () => {
waitController.abort();
workController.abort();
}, { once: true });
try {
return await navigator.locks.request(
"document-sync",
{ signal: waitController.signal },
async () => {
// 锁已授予:后续任务由 workController 自己负责取消。
return await fetch("/api/sync", { signal: workController.signal });
},
);
} finally {
// 无论等待失败还是回调完成,都要清掉定时器和监听副作用。
clearTimeout(timeoutId);
}
}

不要把取消等待误当成取消临界区
| 状态 | 推荐控制 | 结果 |
|---|---|---|
| 请求仍在队列 | request 的 signal | Promise 以 AbortError 拒绝,回调不会启动 |
| 回调已经启动 | 业务专用 AbortController | 停止 fetch 或计算,等待回调自行收尾 |
| 回调即将结束 | finally 清理 | 释放临时资源,锁随回调返回自动释放 |
不要为了“尽快让下一个标签页拿到锁”而使用 steal 作为取消方案。它会抢占同名已持有锁,但原先正在执行的代码仍可能继续运行,反而制造并发写入。需要快速失败时,排队阶段可以使用 ifAvailable;需要可取消等待时,使用 signal 更容易表达真实意图。
上线前的 Web Locks 取消检查清单
- 锁名是否代表真实共享资源,并且所有标签页使用同一命名规则?
- 是否只在等待阶段把
AbortError当作可预期分支? - 回调中的 fetch、定时器、流读取是否有独立的业务取消信号?
- 回调的
finally是否会释放监听器、临时状态和 UI loading? - 是否在 HTTPS 安全上下文和目标浏览器中确认了 Web Locks API 可用性?
常见问题
AbortController.abort() 后为什么回调仍然执行?
通常是因为锁已经在 abort 发生前授予。request 的 signal 不能撤销已经开始的回调,应检查业务信号并让回调尽快返回。
Web Locks 的 signal 可以和 ifAvailable 一起用吗?
不能简单叠加。规范和 MDN 将 signal 与 ifAvailable 或 steal 的组合列为不支持的参数组合;需要快速失败就选 ifAvailable,需要可取消排队就选 signal。
取消等待后要不要立刻重试?
不要把用户取消当成无限重试信号。只有在明确的业务策略允许时,才用退避和新的 AbortController 创建下一次请求。
-
502 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
文章 · 前端 | 6小时前 | javascript · AbortController AbortSignal.any AbortError TimeoutError AbortSignal reason447 收藏
-
110 收藏
-
120 收藏
-
202 收藏
-
257 收藏
-
315 收藏
-
356 收藏
-
158 收藏
-
234 收藏
-
446 收藏
-
225 收藏
-
464 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习