Concurrency Semaphore for Node.js
This package provides a basic counting semaphore mechanism for Node.js environments, designed to limit the number of concurrent operations accessing a shared resource. The current and only stable version, 1.1.0, was published 9 years ago and explicitly targets Node.js 0.8.0. Due to its age and lack of maintenance, it does not follow a regular release cadence and is effectively abandoned for modern Node.js development. Its primary differentiator is its extreme simplicity and minimal footprint, offering fundamental concurrency control without features found in more contemporary, promise-based semaphore implementations (e.g., `async-sema`, `await-semaphore`). While functional for very old projects, it lacks modern JavaScript features and type definitions out of the box.
Common errors
-
ReferenceError: require is not defined
cause Attempting to import the CommonJS-only `semaphore` package in an ES Module context (e.g., a `.mjs` file or a project with `"type": "module"` in `package.json`).fixIf using ES Modules, consider migrating to a modern, promise-based semaphore library that supports `import` statements. If you must use this package, you might need to use dynamic `import('semaphore')` or configure a bundler to transform CommonJS. Ensure your file is a CommonJS module (e.g., `.js` without `"type": "module"` or explicitly `.cjs`). -
TypeError: semaphore is not a function
cause You are trying to use the result of `require('semaphore')` directly as an object or calling methods on it, but it's actually a factory function that needs to be invoked first.fixYou must invoke the result of `require('semaphore')` with a `capacity` argument to create a semaphore instance. Correct usage: `const sem = require('semaphore')(capacity);`.
Warnings
- breaking The package is CommonJS-only and relies on `require()` syntax. It will not work natively in ES Module (ESM) environments without a CJS wrapper or bundler configuration. Modern Node.js projects increasingly use ESM by default.
- gotcha The package is extremely old, last published 9 years ago, targeting Node.js >=0.8.0. It lacks active maintenance, which means potential bugs, security vulnerabilities, or performance issues are unlikely to be addressed.
- gotcha This package does not provide TypeScript type definitions natively. While `@types/semaphore` exists, it's also not actively maintained and may not fully align with modern TypeScript practices.
- gotcha The `sem.take()` method is callback-based. If the callback function throws an error, the semaphore's `leave()` method might not be called, leading to a deadlock where the semaphore never releases its hold and subsequent operations are permanently blocked.
Install
-
npm install semaphore -
yarn add semaphore -
pnpm add semaphore
Imports
- semaphore
import semaphore from 'semaphore';
const semaphore = require('semaphore'); - sem
const sem = new semaphore(capacity);
const sem = require('semaphore')(capacity); - sem.take
sem.take().then(() => { /* ... */ });sem.take(function() { /* ... */ });
Quickstart
const semaphore = require('semaphore');
const http = require('http');
// Limit concurrent database access to 1 operation at a time
const dbSemaphore = semaphore(1);
const expensive_database_operation = (callback) => {
console.log('Starting expensive DB operation...');
setTimeout(() => {
const error = Math.random() > 0.8 ? new Error('Database error!') : null;
console.log('Finished expensive DB operation.');
callback(error, 'Data from DB');
}, 1000);
};
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
dbSemaphore.take(function() {
console.log('Semaphore taken, requesting DB operation...');
expensive_database_operation(function(err, data) {
dbSemaphore.leave();
if (err) {
console.error('Request failed:', err.message);
return res.end(`Error: ${err.message}`);
}
res.end(`Success: ${data}`);
});
});
});
const PORT = process.env.PORT ?? 3000;
server.listen(PORT, () => {
console.log(`Server listening on http://localhost:${PORT}`);
console.log('Try opening multiple tabs to http://localhost:3000 to see concurrency limits in action.');
});