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