JavaScript is single-threaded, but async code still races. Two functions can await at the same time and both update shared state — caches, balances, worker message flows. Those bugs are hard to reproduce.
I built mutex-forge to bring classic mutex and semaphore patterns to async JavaScript and TypeScript.
What it does
- Mutex — exclusive access to a critical section
- Semaphore — limit how many jobs run at once (pools, rate limits)
- runExclusive — acquire, run, and release automatically
- withTimeout — fail if the lock takes too long
- tryAcquire — non-blocking lock attempts
- TypeScript types included
Example: prevent overlapping updates
javascript
const { Mutex } = require('mutex-forge');
const mutex = new Mutex();
let balance = 0;
async function credit(amount) {
await mutex.runExclusive(async () => {
const current = balance;
await someAsyncWork();
balance = current + amount;
});
}
await Promise.all([credit(10), credit(20)]);
// balance === 30
Top comments (1)
This is a great addition to the async JS toolkit — race conditions from overlapping await calls are exactly the kind of bug that's easy to write and painful to debug. Love that mutex-forge includes tryAcquire and withTimeout out of the box, since those are the parts people usually end up hand-rolling themselves. Definitely adding the mutex-forge npm package to my toolbox for the next time I'm dealing with shared state across concurrent async calls.