🚀 DAY 2 OF LAUNCH WEEK: Unify Your Security Stack with Socket Basics.Learn more
Socket
Book a DemoInstallSign in
Socket

@henrygd/semaphore

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@henrygd/semaphore

Fast inline semaphores and mutexes

latest
Source
npmnpm
Version
0.1.0
Version published
Weekly downloads
217
-28.15%
Maintainers
1
Weekly downloads
 
Created
Source

@henrygd/semaphore

File Size MIT license JSR Score 100%

Fast inline semaphores and mutexes. See comparisons and benchmarks.

Semaphores limit simultaneous access to code and resources (e.g. a file) among multiple concurrent tasks.

Works with: browsers Deno Node.js Cloudflare Workers Bun

Usage

Create or retrieve a semaphore by calling getSemaphore with optional key and concurrency limit.

const sem = getSemaphore('key', 1)

Use the acquire and release methods to limit access.

await sem.acquire()
// access here is limited to one task at a time
sem.release()

Full example

We use semaphores here to prevent multiple requests to an API for the same resource.

The first calls to fetchPokemon will acquire access to the protected code. Subsequent calls will wait, then return the data from the cache.

We use a key to allow access based on the name. This lets ditto and snorlax run simultaneously.

import { getSemaphore } from '@henrygd/semaphore'

const cache = new Map()

for (let i = 0; i < 5; i++) {
    fetchPokemon('ditto')
    fetchPokemon('snorlax')
}

async function fetchPokemon(name) {
    // get semaphore with key based on name
    const sem = getSemaphore(name)
    // acquire access from the semaphore
    await sem.acquire()
    try {
        // return data from cache if available
        if (cache.has(name)) {
            console.log('Cache hit:', name)
            return cache.get(name)
        }
        // otherwise fetch from API
        console.warn('Fetching from API:', name)
        const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${name}`)
        const json = await res.json()
        cache.set(name, json)
        return json
    } finally {
        // release access when done
        sem.release()
    }
}

Interface

/**
 * Creates or retrieves existing semaphore with optional key and concurrency level.
 *
 * key - Key used to identify the semaphore. Defaults to `Symbol()`.
 * concurrency - Maximum concurrent tasks allowed access. Defaults to `1`.
 */
function getSemaphore(key?: any, concurrency?: number): Semaphore

interface Semaphore {
    /** Returns a promise that resolves when access is acquired */
    acquire(): Promise<void>
    /** Release access to the semaphore */
    release(): void
    /** Returns the total number of tasks active or waiting for access */
    size(): number
}

Keys and persistence

Keyed semaphores are held in a Map and deleted from the Map once they've been acquired and fully released (no waiting tasks).

If you need to reuse the same semaphore even after deletion from the Map, use a persistent variable instead of calling getSemaphore again.

Concurrency

Concurrency is set for each semaphore on first creation via getSemaphore. If called again using the key for an active semaphore, the concurrency argument is ignored and the existing semaphore is returned.

Comparisons and benchmarks

Note that we're looking at libraries which provide a promise-based locking mechanism, not callbacks.

LibraryVersionBundle size (B)KeysWeekly Downloads
@henrygd/semaphore0.0.1267yes¯\_(ツ)_/¯
async-mutex0.5.04,758no1,639,071
async-sema3.1.13,532no1,258,877
await-semaphore0.1.31,184no60,449
@shopify/semaphore3.1.0604no29,089

If there's a library you'd like added to the table or benchmarks, please open an issue.

Benchmarks

All libraries run the same test. Each operation measures how long it takes a binary semaphore with 1,000 queued acquire requests to allow and release all requests.

Browser benchmark

This test was run in Chromium. Chrome and Edge are the same. Safari is more lopsided with Vercel's async-sema dropping to third. Firefox, though I love and respect it, seems to be hard capped by slow promise handling, with async-mutex not far behind.

You can run or tweak for yourself here: https://jsbm.dev/8bBxR1pBLw0TM

@henrygd/queue - 13,665 Ops/s. async-sema - 8,077 Ops/s. async-mutex - 5,576 Ops/s. @shopify/semaphore - 4,099 Ops/s.

Note: await-semaphore is extremely slow for some reason and I didn't want to include it in the image because it seems excessive. Not sure what's happening there.

Node.js benchmark

@henrygd/queue - 1.7x faster than async-sema. 2.66x async-mutex. 3.08x async-semaphore. 3.47x @shopify/semaphore.

Bun benchmark

@henrygd/queue - 2x faster than async-semaphore 2.63x asynsc-mutex. 2.68x async-sema. 3.77x @shopify/semaphore.

Deno benchmark

@henrygd/queue - 1.7x faster than async-sema. 2.7x async-mutex. 2.72x await-semaphore. 4.01x @shopify/semaphore.

Cloudflare Workers benchmark

Uses oha to make 1,000 requests to each worker. Each request creates a semaphore and resolves 5,000 acquisitions / releases.

This was run locally using Wrangler. Wrangler uses the same workerd runtime as workers deployed to Cloudflare, so the relative difference should be accurate. Here's the repo for this benchmark.

LibraryRequests/secTotal (sec)AverageSlowest
@henrygd/semaphore941.81351.06180.05210.0788
async-mutex569.51301.75590.08620.1251
async-sema375.73322.66150.13080.1818
@shopify/semaphore167.82395.95860.29250.4063
await-semaphore*n/an/an/an/a

* await-semaphore does not work with concurrent requests.

@henrygd/queue - Tiny async queue with concurrency control. Like p-limit or fastq, but smaller and faster.

License

MIT license

Keywords

mutex

FAQs

Package last updated on 14 Sep 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts