Web Locks API

Coordinate work and the use of resources among different processes

Time to lock in

Asynchronous functions in Javascript aren’t truly parallel. Starting with ES2017, Javascript has support for concurrent function calls. The difference is very nicely summed up by Madhavan Nagarajan in the following statement (link can be found in the addendum).

Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.

The implementation of Javascript’s concurrency is reduced in its functionality to allow for a simple and very robust API to use. Async functions can’t be interrupted and have no concept of volatile properties when dealing with access to the same variable by different asynchronous calls. The detailed implementation of ES2017’s async functions is out of scope for this article, but the main point I want to get across is that until now, parallelism wasn’t built into Javascript and the web and therefore, no special handling of parallel mutations of the same entity. But that changes now with the introduction of the “Web Locks API”.

One for all and all for one

Now that progressive web apps offer you true multithreaded architectures with the introduction of WebWorkers and the ServiceWorker, things have changed. True parallelism is possible in web apps, and therefore the desire for more sophisticated tools to deal with parallel code arises.

A lock is a mechanism to request access to resources and have the guarantee that only one process at any point in time really has the access. Locking resources in this way avoids a large set of conflict areas that might else arise.

A resource is just a namespace, identified by a string. And your request to access the resource is just as simple, as you only need the ID and a function callback, most likely asynchronous, to implement the “Web Lock API”. When one async function has finished the lock gets released and other requests can access it again.

/*
 * A simple demonstration of the Web Lock API.
 * Note that this example has been taken from
 * MDN's awesome documentation, linked in the
 * addendum.
 */
async function foo(){
  // A common async function that you already know.
  const data = await getData();

  // Request the lock.
  // Note that we can simply await the locked call!
  await navigator.locks.request('lock_resource_id', async lock => {
    // The lock has been acquired.
    // At this point, this call has exlusive access 
    // to the resource 'lock_resource_id'.
    await updateDb(data);
    // Now the lock will be released.
  });
  
  // The lock has been released.
  // Continuse with plain async calls.
  await updateUi();
}

Due to the experimental state of this new API, you have to treat it as an optional feature in your code with a valid fallback available.

/**
 * Due to the usage of web locks in core parts
 * of your app, actually using the API this way
 * might introduce more problems then it could solve.
 * 
 * Therefore a thorough specification in your app
 * has to be implementation before using this feature.
 */
async function update(){
  if(navigator?.locks){
    // Run logic with lock.
  } else {
    // Use fallback.
  }
}

To better illustrate this new API, we’ll take a look at a simple example. Say you have a multithreaded PWA with your main process and two WebWorkers. Your app also uses an IndexDB, with custom code that syncs changes to and from the database with the cloud. Using the “Web Lock API” in this scenario, every process can request access to the defined lock for syncing changes to the database, making sure that no other mutations get run during this call.

Note that the “Web Lock API” is not so much about mutating a single variable without side effects in your web app (although that can clearly be a use case), but more about handling function calls in a parallel architecture that would otherwise lead to negative side effects.

Much more to learn

This article only gave an introduction to the new "Web Locks API", which is currently in experimental status and mainly supported by Chromium-based browsers. The API offers more options that describe here and is well worth a look into it.

Suggestions

Related

Addendum

Languages