npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wicle/mutex

v0.10.2

Published

Async mutex and semaphore implementation for Node.js and browsers.

Readme

@wicle/mutex

Async Mutex and Semaphore for Node.js and browsers, written in TypeScript.

  • Zero dependencies
  • Promise-based, works with async/await
  • POSIX-style naming (wait, post, lock, unlock) with ergonomic aliases (acquire, release, withLock, …)
  • Timeout variants for every blocking operation

Installation

npm add @wicle/mutex
yarn add @wicle/mutex
pnpm add @wicle/mutex

Quick start

Mutex — exclusive access

The safest pattern is withLock, which releases the lock automatically even if the callback throws.

import { Mutex } from "@wicle/mutex";

const mtx = new Mutex();
let counter = 0;

// Launch 100 concurrent "threads" — only one runs inside withLock at a time.
await Promise.all(
    Array.from({ length: 100 }, () =>
        mtx.withLock(async () => {
            const val = counter;
            await someAsyncWork();
            counter = val + 1; // safe: no other thread can be here simultaneously
        })
    )
);

Manual lock / unlock:

await mtx.lock();
try {
    // critical section
} finally {
    mtx.unlock();
}

Semaphore — resource counting and signaling

Resource counting (initial value > 0): limits the number of concurrent operations.

import { Semaphore } from "@wicle/mutex";

const sem = new Semaphore(3); // allow up to 3 concurrent operations

async function fetchWithLimit(url: string) {
    return sem.withAcquire(async () => {
        const res = await fetch(url);
        return res.json();
    });
}

Signaling (initial value 0): one coroutine waits, another signals.

const ready = new Semaphore(0); // starts blocked

// Consumer waits for the producer to signal.
async function consumer() {
    await ready.wait();
    console.log("producer finished, processing now");
}

// Producer signals when done.
async function producer() {
    await doWork();
    ready.post();
}

await Promise.all([consumer(), producer()]);

API

Semaphore

new Semaphore(value?: number)

Creates a semaphore with value available resources (default 0). A value of 0 is the signaling pattern — the semaphore starts blocked and a caller must post() before wait() can proceed. Use a positive value for resource counting.


sem.wait() → Promise<void>

Acquires one resource, waiting until one is available. POSIX sem_wait.

await sem.wait();

sem.tryWait() → boolean

Acquires one resource without waiting. Returns true if acquired, false if none are available. POSIX sem_trywait.

sem.waitFor(timeout) → Promise<boolean>

Acquires one resource, waiting up to timeout milliseconds. Returns true if acquired, false on timeout.

value must be a non-negative integer, and timeout must be a finite number greater than or equal to 0.

if (await sem.waitFor(500)) {
    // acquired within 500 ms
} else {
    // timed out
}

sem.post() → void

Releases one resource, waking the oldest waiter if any. POSIX sem_post.

sem.value → number

The current resource count — number of available resources, or pending signals in the signaling pattern.

sem.maxValue → number

The initial resource count passed to the constructor. Reflects the pool size for the resource-counting pattern, or 0 for the signaling pattern.

sem.withAcquire(fn) → Promise<T>

Acquires one resource, runs fn, then releases it — even if fn throws. Recommended over manual acquire/release.

Only use withAcquire with a positive initial value. With value = 0 (signaling pattern), acquire() will block until someone calls post(), and there is no automatic signal to unblock it.

const result = await sem.withAcquire(async () => {
    return doWork();
});

Aliases — identical behavior, alternative naming:

| Alias | Equivalent | | ------------------------- | ---------------------- | | sem.acquire() | sem.wait() | | sem.tryAcquire() | sem.tryWait() | | sem.acquireFor(timeout) | sem.waitFor(timeout) | | sem.release() | sem.post() |


Mutex

A binary semaphore with an ownership guard. Equivalent to new Semaphore(1) but with additional safety: unlock() throws if the mutex is not currently locked.

Lock ownership is not enforced — any caller can release the lock. This is intentionally lightweight and is not a re-entrant or owner-tracking mutex.

new Mutex();

mtx.lock() → Promise<void>

Acquires the lock, waiting until it is available.

mtx.tryLock() → boolean

Acquires the lock without waiting. Returns true if acquired, false if already locked.

mtx.lockFor(timeout) → Promise<boolean>

Acquires the lock, waiting up to timeout milliseconds. Returns true if acquired, false on timeout.

if (await mtx.lockFor(500)) {
    try {
        // critical section
    } finally {
        mtx.unlock();
    }
} else {
    // could not acquire within 500 ms
}

mtx.unlock() → void

Releases the lock. Throws if the mutex is not currently locked — use this to catch double-unlock bugs early.

mtx.locked → boolean

true while the mutex is held.

mtx.withLock(fn) → Promise<T>

Acquires the lock, runs fn, then releases — even if fn throws. Recommended over manual lock/unlock.

const result = await mtx.withLock(async () => {
    return doExclusiveWork();
});

License

MIT © 2024 Robin Nam