AJAX - AJAX with Web Locks API

 

1. Introduction

AJAX (Asynchronous JavaScript and XML) is a technique that allows web applications to communicate with a server and exchange data without reloading the entire webpage. Modern AJAX applications often use JavaScript's Fetch API to retrieve data, submit forms, update user interfaces, and perform background operations.

When a web application is opened in multiple browser tabs or windows, a new challenge can arise. Each tab operates independently and may attempt to perform the same operation at the same time. For example, two tabs might try to update the same shared resource, synchronize user information, or refresh application data simultaneously.

The Web Locks API helps address this problem by allowing a web application to coordinate access to shared resources between browser tabs and workers that belong to the same origin. A lock allows one operation to obtain exclusive or shared access to a named resource while other conflicting operations wait.

When used with AJAX, the Web Locks API can help prevent duplicate requests, coordinate data synchronization, and reduce conflicts between concurrent operations.

2. What Is the Web Locks API?

The Web Locks API is a browser API that enables scripts to request locks associated with named resources. It is available through navigator.locks in supported browsers.

A lock can be requested using the navigator.locks.request() method. The browser grants the lock when the requested access is available and executes the supplied callback. When the callback finishes, the lock is automatically released.

For example, suppose a web application has two tabs open. Both tabs need to synchronize a shared set of information with a server. Without coordination, both tabs may send the same AJAX request simultaneously. By requesting the same named lock, the tabs can coordinate their work so that only one performs the protected synchronization operation at a time.

The Web Locks API does not replace AJAX, Fetch, or server-side synchronization. Instead, it coordinates browser-side operations that might otherwise interfere with one another.

3. Why Use the Web Locks API with AJAX?

Web applications frequently perform background operations such as retrieving notifications, refreshing dashboards, synchronizing records, and saving user preferences. When multiple tabs perform these operations independently, unnecessary requests and conflicting updates may occur.

The Web Locks API is useful in several situations:

  • Preventing duplicate requests: Multiple tabs can coordinate before performing the same background operation.

  • Coordinating data synchronization: A shared synchronization task can be restricted to one participating tab at a time.

  • Avoiding conflicting updates: Operations that modify shared browser-side data can be serialized.

  • Reducing unnecessary server traffic: Avoiding redundant work can reduce repeated requests to the server.

  • Maintaining consistent application behavior: Tabs can follow a common coordination strategy for operations involving shared resources.

However, a browser lock does not guarantee that the server will receive only one request under all circumstances. Other devices, browser profiles, or applications may still perform the same operation. The server must enforce critical rules such as preventing duplicate payments or conflicting database updates.

4. How the Web Locks API Works with AJAX

The process can be understood through the following steps.

  1. A tab initiates an operation: A browser tab needs to retrieve or update information from a server.

  2. The tab requests a lock: JavaScript calls navigator.locks.request() with a resource name, such as data-sync-lock.

  3. The browser checks lock availability: If another tab holds a conflicting lock with the same name, the new request waits.

  4. The browser grants the lock: When the lock becomes available, the callback associated with the request begins executing.

  5. The AJAX request runs: The callback uses fetch() to communicate with the server.

  6. The operation finishes: Once the callback completes, the browser releases the lock automatically, allowing another waiting operation to proceed.

This mechanism provides coordination between participating tabs without requiring them to communicate directly with one another.

5. Example: Using Web Locks API with Fetch

Consider a web application in which multiple tabs need to refresh the same dashboard data. The following example uses the Web Locks API to coordinate the request.

async function refreshDashboard() {
    if (!navigator.locks) {
        console.log("Web Locks API is not supported.");
        return;
    }

    try {
        await navigator.locks.request(
            "dashboard-refresh-lock",
            async () => {
                console.log("Lock acquired. Fetching dashboard data.");

                const response = await fetch("/api/dashboard");

                if (!response.ok) {
                    throw new Error(
                        `HTTP error: ${response.status}`
                    );
                }

                const data = await response.json();

                console.log("Dashboard data:", data);

                // Update the dashboard interface here.
            }
        );
    } catch (error) {
        console.error("Dashboard refresh failed:", error);
    }
}

refreshDashboard();

Explanation of the code

Step 1: Define the function

The refreshDashboard() function is declared as asynchronous because it needs to wait for the lock request and the server response.

Step 2: Check browser support

The expression navigator.locks checks whether the browser provides the Web Locks API. If it is unavailable, the function stops rather than attempting to use an unsupported feature.

Step 3: Request a named lock

The statement navigator.locks.request("dashboard-refresh-lock", ...) requests a lock with the name dashboard-refresh-lock. Other participating tabs requesting the same lock in exclusive mode must wait while this operation holds it.

Step 4: Send the AJAX request

Inside the callback, fetch("/api/dashboard") sends an HTTP request to the server. The callback waits for the response and then converts the returned JSON into a JavaScript object.

Step 5: Handle errors

The response.ok check detects unsuccessful HTTP responses, while the catch block handles errors such as network failures and exceptions raised inside the callback.

Step 6: Release the lock

When the callback completes or throws an error, the browser releases the lock automatically. The next waiting request can then acquire it.

An important detail is that the lock remains held while the callback is waiting for the server response. This helps serialize the protected operation, but slow network requests can also delay other tabs waiting for the same lock.

6. What Happens When Multiple Tabs Request the Same Lock?

Suppose Tab A and Tab B both call refreshDashboard() at approximately the same time.

Tab A

Requests dashboard-refresh-lock

 

Browser Lock Manager

Grants the lock to Tab A; Tab B waits.

 

Tab A performs the Fetch request

Receives the response and finishes its callback.

 

Lock released

Tab B can now acquire the lock and perform its own request.

This example illustrates sequential execution, not request deduplication. Both tabs may still send a request; the lock ensures that their protected callbacks do not run concurrently under the same exclusive lock.

If the goal is to let all tabs share one server response rather than make separate requests sequentially, the application needs an additional mechanism, such as shared cached data or cross-tab messaging.

7. Types of Locks

The Web Locks API supports different access modes depending on the requirements of the application.

Lock type

Description

Example use

Exclusive lock

Only one holder can access the named resource at a time.

Updating shared application data.

Shared lock

Multiple holders can hold the same lock concurrently, provided no conflicting exclusive lock is held.

Coordinating operations that only read shared data.

An exclusive lock is the default when using navigator.locks.request() without specifying a mode.

To request a shared lock, the application can use the mode option:

await navigator.locks.request(
    "user-data-lock",
    { mode: "shared" },
    async () => {
        const response = await fetch("/api/user-data");
        const data = await response.json();

        console.log(data);
    }
);

Multiple tabs can hold this shared lock simultaneously. If an exclusive lock is requested for the same resource, the browser's lock manager coordinates the conflicting requests according to its scheduling rules.

Shared locks are appropriate for coordinated read operations, but they do not make the returned data immutable or guarantee that the server's underlying information will remain unchanged.

8. Using Web Locks API for Data Synchronization

Consider a productivity application that synchronizes locally stored changes with a server. If a user opens the application in three tabs, each tab might independently try to synchronize the same data.

This can lead to repeated requests, redundant processing, or conflicting updates.

A lock can coordinate the synchronization operation:

async function synchronizeData() {
    await navigator.locks.request(
        "application-sync-lock",
        async () => {
            const response = await fetch("/api/sync", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json"
                },
                body: JSON.stringify({
                    action: "synchronize"
                })
            });

            if (!response.ok) {
                throw new Error("Synchronization failed");
            }

            const result = await response.json();

            console.log("Synchronization result:", result);
        }
    );
}

When multiple tabs call this function, their synchronization callbacks are serialized through the named lock.

For a production application, the synchronization process should also consider retry behavior, network interruptions, and duplicate submissions. If a failed request is retried, the server may need an idempotency mechanism to prevent the same operation from being applied twice.

9. Handling Lock Contention and Errors

Lock contention occurs when multiple operations compete for the same exclusive lock. The browser queues conflicting requests until the lock becomes available.

Developers should consider the following practices:

  • Keep the work performed inside a lock as short as practical.

  • Avoid waiting indefinitely for unrelated operations while holding a lock.

  • Catch exceptions so that failures can be reported and handled properly.

  • Use meaningful lock names to distinguish independent resources.

  • Consider cancellation through an AbortSignal when an operation should stop waiting for a lock.

  • Use navigator.locks.query() when debugging, where supported, to inspect held locks and pending requests.

For example, a request can be configured with an abort signal:

const controller = new AbortController();

try {
    await navigator.locks.request(
        "report-generation-lock",
        { signal: controller.signal },
        async () => {
            const response = await fetch("/api/report");

            if (!response.ok) {
                throw new Error("Unable to load report");
            }

            const report = await response.json();
            console.log(report);
        }
    );
} catch (error) {
    if (error.name === "AbortError") {
        console.log("Lock request was cancelled.");
    } else {
        console.error("Operation failed:", error);
    }
}

// Call this if the waiting request should be cancelled:
// controller.abort();

The abort signal can cancel a pending lock request. Once a callback has started, aborting that signal does not automatically terminate the callback or its Fetch request. To stop an in-progress network request, the application should also use an AbortController with fetch().

10. Advantages of Using Web Locks API with AJAX

The combination of AJAX and the Web Locks API offers several advantages.

Improved coordination: Multiple tabs can coordinate access to shared browser-side resources.

Reduced overlapping work: Exclusive locks prevent participating operations from executing their protected sections concurrently.

Simpler asynchronous programming: Developers can use promises and async/await instead of implementing a complete locking system themselves.

Automatic lock release: Locks are released when their callbacks finish, including when the callback fails with an exception.

Flexible access control: Shared and exclusive modes support different concurrency requirements.

11. Limitations and Important Considerations

Although the Web Locks API is useful, it does not solve every concurrency problem.

  1. Browser support: Developers should check navigator.locks and provide a fallback when necessary.

  2. Origin restrictions: Lock coordination is limited to the applicable origin and browser context; it does not coordinate unrelated websites or independent browser profiles.

  3. No server-side protection: The API cannot replace database transactions, server-side locking, authorization, or idempotency controls.

  4. Potential waiting delays: A long-running callback can block other operations requesting the same exclusive lock.

  5. No automatic response sharing: A lock serializes access but does not automatically cache a response or distribute it to other tabs.

  6. Security considerations: Locking is a coordination mechanism, not a security boundary. Applications must continue validating user permissions and input on the server.

12. Best Practices

When implementing the Web Locks API with AJAX, developers should use descriptive lock names, keep protected operations focused, handle network failures, and avoid holding locks longer than necessary.

They should also distinguish between coordinating an operation and sharing its result. If the requirement is to ensure that only one tab performs a background refresh and all other tabs receive the resulting data, a combination of Web Locks, shared storage such as IndexedDB, and cross-tab communication such as BroadcastChannel may be more appropriate.

For operations involving financial transactions, inventory, or other critical data, server-side safeguards remain essential because a browser lock only coordinates participating scripts within its scope.

Conclusion

The Web Locks API is a browser feature that helps manage concurrent operations across tabs and workers belonging to the same origin. When integrated with AJAX techniques such as Fetch, it can coordinate background requests, prevent overlapping execution of protected operations, and simplify synchronization logic.

However, developers must understand that locking is not the same as deduplication. The Web Locks API controls when participating browser operations execute, while response caching, cross-tab communication, and server-side consistency require additional mechanisms. Used appropriately, it is a valuable tool for building reliable and responsive multi-tab web applications.