Today I Learned - Rocky Kev

TIL about semaphores

POSTED ON:

TAGS:

What is a Semaphore?

A semaphore limits access to a resource to some number of consumers. Any new requests for the resource will get stalled until later, eventually to be handled.

ELI5 version:

A semaphore is kind of like a revolving door at an office building: they make sure that only one person at a time is passing through the door way, at any given time.

With a normal door, sometimes, someone is trying to walk through, while someone else is trying to walk through in the opposite direction. This would cause what is commonly referred to as a "dead lock".

With the revolving door, you can only go through it if there is a space for you to stand in, as it spins around. You will get through the door, you may just have to wait for an available space, regardless of whether you're entering the building, or leaving.

via indienick

Use cases

One major usecase is managing api usage. If 10,000 hit your api at the same time, that's literally a DDOS attack.

But by setting up a Semaphore, you can provide service for a large chunk, and the rest can either wait, get thrown in a queue, ignored, etc.

Code

export default class Semaphore {
/**
* Creates a semaphore that limits the number of concurrent Promises being handled
* @param {*} maxConcurrentRequests max number of concurrent promises being handled at any time
*/

constructor(maxConcurrentRequests = 1) {
this.currentRequests = [];
this.runningRequests = 0;
this.maxConcurrentRequests = maxConcurrentRequests;
}

/**
* Returns a Promise that will eventually return the result of the function passed in
* Use this to limit the number of concurrent function executions
* @param {*} fnToCall function that has a cap on the number of concurrent executions
* @param {...any} args any arguments to be passed to fnToCall
* @returns Promise that will resolve with the resolved value as if the function passed in was directly called
*/

callFunction(fnToCall, ...args) {
return new Promise((resolve, reject) => {
this.currentRequests.push({
resolve,
reject,
fnToCall,
args,
});
this.tryNext();
});
}

tryNext() {
if (!this.currentRequests.length) {
return;
} else if (this.runningRequests < this.maxConcurrentRequests) {
let { resolve, reject, fnToCall, args } = this.currentRequests.shift();
this.runningRequests++;
let req = fnToCall(...args);
req.then((res) => resolve(res))
.catch((err) => reject(err))
.finally(() => {
this.runningRequests--;
this.tryNext();
});
}
}
}

/* HOW TO USE */
const throttler = new Semaphore(2);
throttler.callFunction(fetch, 'www.facebook.com');
throttler.callFunction(fetch, 'www.amazon.com');
throttler.callFunction(fetch, 'www.netflix.com');
throttler.callFunction(fetch, 'www.google.com');

github gist

via Weiming Wu's Semaphores in JavaScript


Related TILs

Tagged:

TIL what is npm Script

Despite their high usage they are not particularly well optimized and add about 400ms of overhead. In this article we were able to bring that down to ~22ms.

TIL fancy methods to transform Javascript Objects

You can use Object.entries(), Object.keys(), Object.fromEntries()...

TIL how to hide your JS code

ONE THING TO NOTE: Encrypting a script is stronger than obfuscation, both methods are still not adequate to protect secret content. Honestly, I don't think it's worth it on a production site, and instead just go with pure server-side if you want security. But it's fascinating.