Web Locks API

安全上下文:此功能仅在安全上下文(HTTPS)中可用,且在某些或所有支持的浏览器中可用。

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

Web Locks API允许在一个选项卡或工作线程中运行的脚本异步获取锁,在执行工作时保持锁,然后释放锁。在持有锁期间,同一来源中执行的其他任何脚本都无法获取相同的锁,这允许在多个选项卡或工作线程中运行的 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.
});

在持有锁时,来自此执行上下文或其他选项卡/工作线程对相同锁的请求将被排队。只有在释放锁后,第一个排队的请求才会被授予。

API 提供了可根据需要使用的可选功能,包括

  • 从异步任务返回的值
  • 共享和独占锁模式
  • 条件获取
  • 诊断以查询来源中锁的状态
  • 防止死锁的紧急出口

锁的作用域为来源;从 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() 方法来内省来源的锁管理器的状态。这在调试时非常有用,例如,确定无法获取锁的原因。结果是锁管理器状态的快照,在拍摄快照时识别已持有和请求的锁以及有关每个锁的其他一些数据(例如模式)。

高级用法

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

js
// Capture promise control functions:
let resolve, reject;
const p = new Promise((res, rej) => {
  resolve = res;
  reject = rej;
});

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

死锁

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

接口

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

LockManager

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

扩展到其他接口

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

WorkerNavigator.locks 只读

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

规范

规范
Web Locks API

浏览器兼容性

api.LockManager

BCD 表格仅在启用 JavaScript 的浏览器中加载。

api.Lock

BCD 表格仅在启用 JavaScript 的浏览器中加载。