Asynchronous Locks in JavaScript
Once upon a time, there was a language named JavaScript that run in a single thread. Early enough in JavaScript’s life, events helped circumvent the issue by allowing asynchronous executions. This also brought tons of headaches. Over the time callbacks, Promises and eventually async functions. All of them easing our way into our modern asynchronous way of coding with JavaScript.
Anyway, the problem I found, similarly to Christophe Marois, was that I needed my code to not asynchronous. Not only that, ideally to establish a critical section that would be under execution only one at a time. Rewriting modules without end was not really the answer. In a world full of asynchronous functions, I needed synchronicity back.
Chris’ solution was partially working for me. My personal issue is that once a lock is “disabled” then all the “threads” waiting would be immediately trigger. And so, I decided to put a queue on it.
Get Gerardino Illescas’s stories in your inbox
Join Medium for free to get updates from this writer.
So, without further ado, here’s my updated version of a home-made asynchronous lock using promises in JavaScript.
class AsyncLock {
constructor() {
this.queue = [];
}
addToQueue() {
return new Promise((resolve) => {
this.queue.push(resolve);
});
}
acquire() {
if (!this.queue.length) {
this.queue.push(() => {});
return Promise.resolve();
} else {
return this.addToQueue();
}
}
release() {
this.queue.shift();
if (this.queue.length) {
this.queue[0]();
}
}
}How to use it, you ask?
// Here's a quick sleep function that will help us pass some time
const sleep = async (timeout = 3000) => new Promise((resolve) => setTimeout(resolve, timeout));
// Create a new lock
const lock = new AsyncLock();
// Make an async function to test
async function test(workerNumber) {
console.log(`Starting test for worker ${workerNumber}`);
// Let's open the critical section
await lock.acquire();
console.log(`Inside critical section for worker ${workerNumber}, doing some work...`);
await sleep();
console.log(`... and ${workerNumber} is done.`);
// Releasing
lock.release();
}
// Call the async function using worker number to identify each function.
test(1);
test(2);
test(3);The output should look something like:
Starting test for worker 1
Starting test for worker 2
Starting test for worker 3
Inside critical section for worker 1, doing some work...
... and 1 is done.
Inside critical section for worker 2, doing some work...
... and 2 is done.
Inside critical section for worker 3, doing some work...
... and 3 is done.What does this mean? That even though our test functions are asynchronous and could have been performed “in parallel”, they are not. The first one takes hold of the lock and until it’s done and releases the lock, then the next one in the queue can pick it up.
Got any thoughts?
It probably looks silly coming from the perspective of languages like Java that have locks built-in. Is almost as…




