Tiny Worker for Node.js
tiny-worker is a lightweight JavaScript library that provides a Web Worker-like API for Node.js environments, allowing developers to offload CPU-intensive tasks to separate threads, similar to the browser's `Worker` interface. It leverages Node.js's `child_process.fork()` module internally, offering a familiar `onmessage`, `postMessage`, and `terminate` API. The current stable version is 2.3.0, with its release cadence historically driven by feature enhancements and bug resolutions rather than a fixed schedule. A key differentiator is its flexibility in module loading within worker scripts, supporting Node.js `require()` by default and offering an `esm: true` option to enable ES6 `import`/`export` syntax, catering to diverse project setups. It is specifically designed for server-side use cases to improve application responsiveness by preventing the main event loop from being blocked by computationally intensive operations.
Common errors
-
An orphaned child process that lives on past the parent process' lifespan
cause The main Node.js process terminated abruptly without explicitly terminating its `tiny-worker` child processes.fixAdd `process.on('SIGINT', () => { worker.terminate(); process.exit(0); });` (and `SIGTERM`) to your main application to ensure workers are shut down cleanly on process termination. -
ReferenceError: require is not defined
cause You are attempting to use CommonJS `require()` syntax inside a worker script that was initialized with the `{ esm: true }` option, forcing an ES Module context.fixRewrite the worker script to use ES Module `import`/`export` syntax, or remove the `{ esm: true }` option from the `Worker` constructor if CommonJS is intended. -
SyntaxError: Cannot use import statement outside a module
cause You are attempting to use ES Module `import`/`export` syntax inside a worker script that was *not* initialized with the `{ esm: true }` option, causing it to run in a CommonJS context.fixWhen creating the worker from a file, pass `{ esm: true }` in the worker options: `new Worker('worker.js', [], { esm: true });`. -
Debugger listening on ws://127.0.0.1:xxxx/ws-id-xxxx... Waiting for the debugger to disconnect...
cause Debugging ports are conflicting between the parent and child processes, or the debugger is not correctly attaching to the child process due to port issues.fixConfigure debug port redirection using `Worker.setRange(min, max)` in the main process. Alternatively, explicitly set `execArgv` with `--inspect=PORT` and `noDebugRedirection: true` in worker options to assign a unique, non-conflicting port.
Warnings
- gotcha Workers created by `tiny-worker` are Node.js child processes. If the main process exits without explicitly terminating workers, they can become orphaned. This is a common cause of lingering processes, consuming system resources.
- gotcha Debugging Node.js child processes requires separate debug ports. `tiny-worker` attempts automatic port redirection to avoid conflicts, but this can cause issues or require manual configuration if specific port ranges or debugging setups are needed.
- gotcha The default module system for scripts executed within `tiny-worker` is CommonJS (`require()`). If you intend to use ES Modules (`import`/`export`) syntax in your worker files, you must explicitly enable it.
- gotcha Worker scripts run in an isolated scope, analogous to browser Web Workers. While Node.js global objects like `process` are available, `self` is the direct global context for worker-specific APIs like `onmessage` and `postMessage`, not `global` or `window`.
Install
-
npm install tiny-worker -
yarn add tiny-worker -
pnpm add tiny-worker
Imports
- Worker
const Worker = require('tiny-worker');import { Worker } from 'tiny-worker'; - Worker.setRange
import { setRange } from 'tiny-worker'; setRange(min, max);import { Worker } from 'tiny-worker'; Worker.setRange(min, max); - Worker script (ESM)
const someHelper = require('./helper');import { someHelper } from './helper.js';
Quickstart
import { Worker } from 'tiny-worker';
// The worker's code is defined as a function. This function will be serialized and run in a separate Node.js child process.
const workerFunction = function () {
// In a worker thread, 'self' refers to the global scope, similar to Web Workers.
self.onmessage = function (ev) {
console.log(`Worker ${process.pid} received: ${ev.data}`);
// Simulate some work, e.g., reversing a string
const result = ev.data.split('').reverse().join('');
postMessage(`Worker ${process.pid} processed: ${result}`);
};
self.onerror = function (err) {
console.error(`Worker ${process.pid} error:`, err.message);
};
console.log(`Worker ${process.pid} started.`);
};
// Create a new worker instance using the function above.
const worker = new Worker(workerFunction);
// Attach a message handler to receive messages back from the worker.
worker.onmessage = function (ev) {
console.log('Main thread received:', ev.data);
worker.terminate(); // Terminate the worker once the task is complete.
};
// Attach an error handler for errors originating from the worker.
worker.onerror = function (err) {
console.error('Main thread error:', err.message);
worker.terminate();
};
// Post a message to the worker to start a task.
worker.postMessage("Hello from the main thread!");
// Add process listeners to ensure graceful shutdown of workers if the main process is terminated.
process.on('SIGINT', () => {
console.log('Main process SIGINT received, terminating worker...');
worker.terminate();
process.exit(0);
});
process.on('SIGTERM', () => {
console.log('Main process SIGTERM received, terminating worker...');
worker.terminate();
process.exit(0);
});