Web Locks API

安全上下文: 此功能仅在安全上下文(HTTPS)中可用,且支持此功能的浏览器数量有限。

注意:此功能在 Web Workers 中可用。

Web Locks API 允许在一个标签页或 worker 中运行的脚本异步地获取一个锁,在执行工作时持有该锁,然后释放它。在持有期间,同一来源(origin)中的任何其他脚本都无法获取相同的锁,这使得在多个标签页或 worker 中运行的 Web 应用程序能够协调工作和资源的使用。

概念与用法

锁是一个抽象概念,代表一些可能被共享的资源,由 Web 应用程序选择的名称来标识。例如,如果一个在多个标签页中运行的 Web 应用程序希望确保只有一个标签页在网络和 Indexed DB 之间同步数据,每个标签页都可以尝试获取一个 "my_net_db_sync" 锁,但只有一个标签页会成功(这是领导者选举模式)。

API 的用法如下:

  1. 请求锁。
  2. 在异步任务中持有锁并执行工作。
  3. 任务完成后,锁会自动释放。
js
navigator.locks.request("my_resource", async (lock) => {
  // The lock has been acquired.
  await do_something();
  await do_something_else();
  // Now the lock will be released.
});

在持有锁期间,来自此执行上下文或来自其他标签页/worker 的相同锁的请求将被排队。第一个排队的请求将在锁释放后才会被授予。

API 提供了一些可选功能,可以按需使用,包括:

  • 从异步任务返回数值
  • 共享和独占锁模式
  • 条件性获取
  • 用于查询一个源(origin)的锁状态的诊断信息
  • 防止死锁的“逃生舱口”

锁是按源(origin)作用域的;从 https://example.com 获取的锁对从 https://example.org:8080 获取的锁没有影响,因为它们是不同的源。

主要入口点是 navigator.locks.request(),它用于请求一个锁。它接受一个锁名、一组可选的选项和一个回调函数。当锁被授予时,会调用回调函数。当回调函数返回时,锁会自动释放;通常回调函数是一个异步函数,这意味着锁只会在异步函数完全完成后才会被释放。

request() 方法本身返回一个 promise,该 promise 在锁被释放后解析;在异步函数中,脚本可以 await 该调用,使异步代码按顺序流动。例如:

js
await do_something_without_lock();

// Request the lock.
await navigator.locks.request("my_resource", async (lock) => {
  // The lock has been acquired.
  await do_something_with_lock();
  await do_something_else_with_lock();
  // Now the lock will be released.
});
// The lock has been released.

await do_something_else_without_lock();

选项

在请求锁时可以传递几个选项:

  • mode:默认模式是 "exclusive"(独占),但可以指定 "shared"(共享)。一个锁只能有一个 "exclusive" 持有者,但可以同时授予多个 "shared" 请求。这可用于实现读写锁模式
  • ifAvailable:如果指定,则在无法立即授予锁而无需等待的情况下,锁请求将失败。回调函数将使用 null 调用。
  • steal:如果指定,则具有相同名称的任何已持有锁将被释放,并且请求将被授予,从而抢占任何已排队的该锁的请求。
  • signal:可以传入一个 AbortSignal,允许中止锁请求。这可用于实现请求超时。

监控

脚本可以使用 navigator.locks.query() 方法来内省该源(origin)的锁管理器状态。这在调试时非常有用,例如,可以用来确定为什么锁无法被获取。结果是锁管理器状态的快照,它标识了在快照拍摄时已持有和请求的锁以及有关每个锁的其他一些数据(例如模式)。

高级用法

对于更复杂的情况,例如持有锁任意时长,回调函数可以返回一个由脚本显式解析的 promise:

js
// Capture promise control functions:
const { promise, resolve, reject } = Promise.withResolvers();

// Request the lock:
navigator.locks.request(
  "my_resource",
  // Lock is acquired.
  (lock) => promise, // Now lock will be held until either resolve() or reject() is called.
);

死锁

死锁发生在进程由于每个部分都在等待一个无法满足的请求而无法继续进行时。在使用此 API 的复杂用例中可能会发生这种情况,例如,如果以乱序请求多个锁。如果标签页 1 持有锁 A,标签页 2 持有锁 B,然后标签页 1 尝试获取锁 B,而标签页 2 尝试获取锁 A,则这两个请求都无法被授予。 Web 应用程序可以通过几种策略来避免这种情况,例如确保锁请求不嵌套,或者始终按良好顺序请求,或者设置超时。请注意,此类死锁仅影响锁本身和依赖于它们的代码;浏览器、其他标签页和页面中的其他脚本不受影响。

接口

Lock

LockManager.request() 的回调函数中接收,提供之前请求的锁的名称和模式。

LockManager

提供用于请求新的 Lock 对象和查询现有 Lock 对象的方法。要获取 LockManager 的实例,请调用 navigator.locks

其他接口的扩展

返回一个 LockManager 对象,该对象提供用于请求新的 Lock 对象和查询现有 Lock 对象的方法。

WorkerNavigator.locks 只读

返回一个 LockManager 对象,该对象提供用于请求新的 Lock 对象和查询现有 Lock 对象的方法。

规范

规范
Web Locks API

浏览器兼容性

api.LockManager

api.Lock