Web Locks API: JavaScript Mutual Exclusion Explained
The Web Locks API is a native browser interface that enables asynchronous JavaScript code across multiple browser contexts—such as tabs, windows, iframes, and Web Workers—to coordinate resource access using mutual exclusion. By allowing scripts to acquire, hold, and release named locks, the API prevents race conditions and data corruption when multiple execution threads or tabs interact with shared resources like IndexedDB, Cache API, or BroadcastChannel.
The Need for Mutual Exclusion in Browsers
JavaScript in the browser runs on a single-threaded event loop per execution context, but modern web applications often run across multiple contexts simultaneously. When a user opens multiple tabs of the same web application, each tab executes its own JavaScript thread while sharing the same underlying origin storage (such as IndexedDB or LocalStorage).
Without proper synchronization, concurrent writes or
read-modify-write sequences from separate tabs can lead to severe race
conditions. Traditionally, developers attempted to manage this with
workarounds like localStorage polling or
SharedWorker coordination, which were inefficient, fragile,
and prone to deadlocks when tabs crashed. The Web Locks API provides a
built-in, browser-level solution designed specifically for this
problem.
How the Web Locks API Operates
The API is accessed via the navigator.locks interface.
Its primary method is navigator.locks.request(), which
queues a request for a lock identified by a unique string name.
navigator.locks.request('user_profile_sync', async (lock) => {
// Critical section: only one context runs this code at a time
const data = await readProfileData();
const updatedData = processUpdates(data);
await saveProfileData(updatedData);
// The lock is automatically released when the returned Promise settles
});The lifecycle of a Web Lock consists of three main phases:
- Request: The script requests a lock with a specific name. If no conflicting lock is held, it is granted immediately. If a conflicting lock exists, the request waits in a FIFO queue.
- Execution: Once acquired, the browser invokes the provided callback function, passing the lock object. The callback represents the “critical section” of the code.
- Release: The lock is automatically released as soon as the callback’s returned Promise resolves or rejects. If the tab or worker crashes, the browser immediately releases the lock, avoiding permanent deadlocks.
Lock Modes: Exclusive vs. Shared
The Web Locks API supports two modes of operation:
- Exclusive Locks (Default): Only one context can hold the lock at any given time. Any other request for an exclusive or shared lock with the same name must wait. This mode is used for write operations.
- Shared Locks: Multiple contexts can hold a shared lock simultaneously under the same name, provided no exclusive lock is held or requested ahead in the queue. This is ideal for read-only operations where concurrent access is safe, but writes must be blocked.
// Requesting a shared lock for reading
await navigator.locks.request('user_profile_sync', { mode: 'shared' }, async (lock) => {
const data = await readProfileData();
console.log('Read profile safely:', data);
});Advanced Options and Capabilities
The Web Locks API includes features to handle edge cases and optimize performance:
- Non-blocking Requests (
ifAvailable): By setting{ ifAvailable: true }, the request will immediately invoke the callback withnullif the lock cannot be granted without waiting, allowing for fallback logic. - Lock Cancellation (
signal): AnAbortSignalcan be passed via{ signal: abortController.signal }to cancel a waiting request if a timeout is reached. - Lock Stealing (
steal): An exclusive lock request can pass{ steal: true }to release existing locks and grant access immediately, primarily intended for cleanup and leader-election routines during failover. - State Inspection (
query): Thenavigator.locks.query()method returns a snapshot of all currently held locks and queued requests across all contexts within the origin, providing visibility for monitoring and debugging.