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

auto-semaphore

v1.0.1

Published

A simple and powerful semaphore implementation for TypeScript.

Readme

auto-semaphore

A simple and powerful semaphore implementation for TypeScript.

Installation

yarn add auto-semaphore

Usage

import { Semaphore } from "auto-semaphore";

const semaphore = new Semaphore(2); // Allow 2 concurrent tasks

const taskIds = [0, 1, 2, 3, 4];

// Run tasks through the semaphore
for (const task of tasks) {
  semaphore.run(async () => {
    console.log(`Task ${id} started`);
    await new Promise(resolve => setTimeout(resolve, 1000));
    console.log(`Task ${id} finished`);

    // Log progress as each task completes.
    // Note: `progress` is updated after the task finishes.
    console.log(`Progress: ${semaphore.progress + 1} / ${semaphore.total}`);

    return `Task ${id} result`;
  });
};

// Collect results once all tasks are done
const results = await semaphore.collect();
console.log('\nAll tasks completed. Results:', results);

// Expected output (order of finished tasks may vary):
// Task 0 started
// Task 1 started
// Task 0 finished
// Progress: 1 / 5
// Task 2 started
// Task 1 finished
// Progress: 2 / 5
// Task 3 started
// Task 2 finished
// Progress: 3 / 5
// Task 4 started
// Task 3 finished
// Progress: 4 / 5
// Task 4 finished
// Progress: 5 / 5
//
// All tasks completed. Results: [ 'Task 0 result', 'Task 1 result', 'Task 2 result', 'Task 3 result', 'Task 4 result' ]

API

run()

Use run() to add a task to the queue. It's a "fire-and-forget" method that executes the task as soon as a slot is available. It returns a Promise for the task's result, but the recommended way to handle completion is by using semaphore.collect().

You can also manually collect the promises returned by run() if you need more granular control over which tasks to await, but using collect() or all is preferred for most cases. See the section below for more details.

enqueue()

Use enqueue() when you need to coordinate your code with the start of a task. Calling await semaphore.enqueue(task) will pause execution until the semaphore has a free slot and the task has begun running.

This provides a backpressure mechanism, ensuring that you don't queue up more work until there's capacity to start it. enqueue returns a Promise that resolves to a function, which in turn returns the Promise for the task's result.

for (let i = 0; i < 5; i++) {
  // Execution will pause here until a slot is available and the task starts.
  const getResult = await semaphore.enqueue(() => someAsyncTask(i));
  console.log(`Task ${i} has been enqueued and started.`);

  // To get the result, you call the returned function
  getResult().then(result => console.log(result))
}

In the example above, you'll see "Task X has been enqueued and started." messages appear as slots become available, demonstrating the backpressure.

waitForSlot()

The waitForSlot() method offers low-level access to the semaphore's slot management. When called, it returns a promise that resolves as soon as a concurrency slot becomes available. This is particularly useful when you need to manually control resource allocation before executing a task.

By calling await semaphore.waitForSlot(), you can pause your code until it's safe to proceed with a resource-intensive operation, without immediately queueing a function. This separates the act of acquiring a slot from executing the task, giving you more fine-grained control over your concurrency logic. Remember to call the release method inside a finally block to ensure the slot is freed up, even if errors occur.

[!CAUTION] The release method is not exposed as part of the public API and is only available on the class instance.

try {
	await semaphore.waitForSlot();
	// Safely execute task here
} finally {
	// The release method is private, so this is just for demonstration
	// semaphore.release();
}

Collecting Results and Monitoring Progress

The Semaphore class provides several helpers to get results and track progress.

The primary way to get results is with collect(), which returns a Promise that resolves when all tasks are complete.

// Wait for all tasks to finish and get results
const results = await semaphore.collect();
// Alternatively
const results = await Promise.all(semaphore.tasks)
// Or if don't want to throw if some of the tasks fail
const results = await Promise.allSettled(semaphore.tasks)

While tasks are running, you can monitor their status using the progress and total getters. total returns the number of tasks added, and progress returns the number of tasks that have finished.

A simple way to track progress is to log it from within the task itself, as shown in the main Usage example. This avoids the need for external timers and keeps the logic self-contained.

Development

This project uses semantic-release for automated versioning and package publishing. Please use the yarn commit command to create commit messages that follow the Conventional Commits specification.

Scripts

  • yarn build: Build the project.
  • yarn test: Run tests.
  • yarn test:watch: Run tests in watch mode.
  • yarn commit: Create a new commit with conventional-commit format.

License

MIT