mutexkit
v0.1.1
Published
Fair async Mutex and counting Semaphore with weighted permits, runExclusive, and AbortSignal support. Zero dependencies.
Maintainers
Readme
mutexkit
Fair async Mutex and counting Semaphore — weighted permits,
runExclusive, andAbortSignalsupport. Zero dependencies.
JavaScript is single-threaded, but async still interleaves — two handlers can
read-modify-write the same resource between awaits and corrupt it. mutexkit
gives you a lock to serialize a critical section, and a semaphore to cap
how many things touch a resource at once.
import { Mutex } from "mutexkit";
const mutex = new Mutex();
// No matter how many callers, the file is appended to one at a time, in order.
await mutex.runExclusive(() => appendLine(file, line));Why mutexkit?
- Mutex & Semaphore in one tiny package.
Mutexfor one-at-a-time;Semaphore(n)for at-most-n. runExclusivedoes the right thing. Acquires, runs your function, and always releases — even if it throws.- Weighted permits. Charge more than one permit per acquire (e.g. memory-sized jobs against a budget).
- Fair & cancellable. FIFO ordering so nobody starves; pass an
AbortSignalto give up waiting.release()is idempotent — double-calling never over-releases. - Zero dependencies, ESM + CJS + types.
Install
npm install mutexkit
# or: pnpm add mutexkit / yarn add mutexkit / bun add mutexkitAPI
Mutex
const mutex = new Mutex();
await mutex.runExclusive(async () => { /* critical section */ });
const release = await mutex.acquire(); // manual form
try { /* ... */ } finally { release(); }
mutex.tryAcquire(); // Release | null (null if held)
mutex.isLocked; // boolean
mutex.pending; // queued waitersacquire / runExclusive accept { signal } to cancel a wait.
Semaphore
import { Semaphore } from "mutexkit";
const sem = new Semaphore(5); // at most 5 concurrent
await Promise.all(tasks.map((t) => sem.runExclusive(() => t())));
const release = await sem.acquire({ weight: 2 }); // take 2 permits
release();
sem.tryAcquire(2); // Release | null
sem.available; // free permits
sem.pending; // queued waitersacquire / runExclusive accept { signal, weight }.
When to reach for which
| Need | Use |
| ------------------------------------------------- | ---------------------------- |
| Only one execution of a critical section at a time | Mutex |
| At most N concurrent | Semaphore(N) |
| Cap concurrency while mapping a list | runpool |
| Cap rate (per second) | ratebucket |
Contributors ✨
This project follows the all-contributors specification. Contributions of any kind are welcome — code, docs, bug reports, ideas, reviews! See the emoji key for how each contribution is recognized, and open a PR or issue to get involved.
Thanks goes to these wonderful people:
License
MIT © Tung Tran
