Skip to content

Web Locks(navigator.locks) ​

同源下跨标签页 / Worker 的互斥协调 API。用于同一时刻只允许一个上下文占用某共享资源,避免多页同时改同一状态。

入口:navigator.locks(LockManager)。需要安全上下文(HTTPS);Worker 中也可用。

适用场景 ​

  • 多标签页同时对同一资源做写操作(大文件断点续传、IndexedDB / localStorage 写入)
  • 文档编辑器:同一文档在多个标签页打开时的编辑权
  • 单页内串行化一批异步任务(批量上传、串联请求)

为何不用常见跨页方案 ​

方案主要问题
localStorage + 轮询读改写非原子,易竞态;崩溃后锁可能残留;需轮询
Broadcast Channel需自建心跳、崩溃检测与冲突解决,无法原子抢占

Web Locks 由浏览器管理加锁 / 排队 / 释放,回调结束(含 async 完成或抛错)后自动释放。

request ​

js
request(name, callback)
request(name, options, callback)
  • name:锁名,同名锁在同源内共享调度
  • callback:获锁后执行;通常为 async,锁持有到该函数完全结束
  • 返回值:Promise,在锁释放后以 callback 的返回值 settle
js
await navigator.locks.request('upload:file-id', async (lock) => {
  // 持有锁期间执行业务
})

options ​

选项说明
mode"exclusive"(默认)或 "shared"。排他锁同时只能有一个持有者;共享锁可并存,但不能与排他锁并存
ifAvailabletrue 时仅在可立即获锁时授予;否则 callback 收到 null,不排队等待。默认 false
stealtrue 时强制释放同名已持有锁并授予当前请求。原持有方代码仍可能继续跑,易冲突,慎用
signalAbortSignal;请求尚未授予时可中止
js
// 抢不到就跳过(多标签页上传:仅一个页真正上传)
await navigator.locks.request('upload:file-id', { ifAvailable: true }, async (lock) => {
  if (!lock) return
  await upload()
})

读写锁模式:

js
// 多读
await navigator.locks.request('doc', { mode: 'shared' }, async () => {
  /* read */
})

// 单写
await navigator.locks.request('doc', { mode: 'exclusive' }, async () => {
  /* write */
})

持有时长 ​

锁在 callback 返回后释放。长任务应在 callback 内 await 到真正结束,而不是 fire-and-forget:

js
await navigator.locks.request(LOCK_NAME, { ifAvailable: true }, async (lock) => {
  if (!lock) return
  await runUntilComplete() // 完成后才释放
})

标签页崩溃时,浏览器会释放该页持有的锁,无需手动清理残留标记。

query ​

js
const state = await navigator.locks.query()
// state.held / state.pending:当前已持有与排队中的锁信息

调试或观测用;业务路径优先依赖 request 的调度结果。

边界 ​

  • 作用域是同源,不是跨站
  • 默认 exclusive;多读者场景才用 shared
  • ifAvailable: true 时务必判断 lock === null
  • steal 不中断旧回调,只抢锁名,不适合作为常规冲突策略

参阅 ​

基于 MIT 许可发布