Provide WebWorker and Node Worker Threads wrapper with better usability. Include:
- WebWorker polyfill/ponyfill for Node.js environment.
- Worker thread pool implementation for both browser and Node.js environment.
npm install @cloudpss/workerIn both Node.js and the browser you can use the ponyfill API instead of the global Worker:
// main.ts
import { Worker } from '@cloudpss/worker/ponyfill';
const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' });
worker.addEventListener('message', (ev) => {
console.log('got from worker:', ev.data);
});
worker.postMessage({ hello: 'world' });// worker.ts
import { onMessage, postMessage } from '@cloudpss/worker/ponyfill';
onMessage((value) => {
// `value` is the `data` of the incoming MessageEvent
postMessage({ echo: value });
});If you prefer to use the standard Worker global in Node.js, import the polyfill once at startup:
import '@cloudpss/worker/polyfill';
// Now `Worker` is available on globalThis in Node.js
const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' });The worker code can still use the ponyfill helpers:
import { onMessage, postMessage } from '@cloudpss/worker/ponyfill';
onMessage((value) => {
postMessage({ ok: true, value });
});The worker pool lets you run many small tasks on a set of shared workers with automatic scaling and cleanup.
Define the worker script and expose the API:
// pool-worker.ts
import { expose } from '@cloudpss/worker/pool';
export default expose({
async sleep(ms: number, value?: unknown) {
await new Promise((resolve) => setTimeout(resolve, ms));
return value;
},
sum(...values: number[]) {
return values.reduce((a, b) => a + b, 0);
},
});Create a pool and call the exposed methods from the main thread:
// main.ts
import { WorkerPool, type WorkerInterface } from '@cloudpss/worker/pool';
// Use import type to avoid runtime dependency
import type WorkerAPI from './pool-worker.js';
const pool = new WorkerPool<typeof WorkerAPI>(
// Use a factory that creates a new Worker instance to make bundlers deal with it correctly
() => new Worker(new URL('./pool-worker.js', import.meta.url)),
{
maxWorkers: 4,
},
);
const result = await pool.call('sleep', [100, 'hello']);
console.log(result); // => 'hello'
// When you are done with the pool
pool.destroy();class Worker extends EventTarget- Compatible with the standard Web Worker API (
postMessage,terminate,message/messageerror/errorevents). - Uses
worker_threadsunder Node.js and the nativeWorkerin the browser.
- Compatible with the standard Web Worker API (
function onMessage(handler: (value: unknown) => unknown): void- Helper for worker-side code. Subscribes to the
messageevent and passesevent.datato the handler.
- Helper for worker-side code. Subscribes to the
function postMessage(value: unknown, transfer?: Transferable[]): void- Worker-side helper that forwards to the underlying
postMessageimplementation.
- Worker-side helper that forwards to the underlying
const IS_WORKER_THREAD: booleantruewhen running inside a worker,falsein the main thread.
const HARDWARE_CONCURRENCY: number- Estimated number of hardware threads (
navigator.hardwareConcurrencyin browsers,os.availableParallelism()in Node.js).
- Estimated number of hardware threads (
- Side-effect-only module.
- Defines
globalThis.Workerusing the ponyfill implementation when it does not already exist (primarily for Node.js).
-
class WorkerPool<T extends WorkerInterface = WorkerInterface>constructor(source: WorkerSource, options?: WorkerPoolOptions)source: JavaScript worker source, such as:- A string of worker code.
- A
Blobcontaining the code. - A
URL(includingdata:orblob:URLs). - A factory function returning any of the above or an existing
Worker/ponyfillWorker.
options: seeWorkerPoolOptionsbelow.
call<M extends WorkerMethods<T>>(method: M, args: Parameters<WorkerMethod<T, M>>, transfer?: Transferable[])- Enqueues a call to
methodon the pool and returns aPromiseof the result.
- Enqueues a call to
callWorker(...)- Low-level variant that calls a specific
Workerinstance from the pool.
- Low-level variant that calls a specific
status(): { total: number; idle: number; busy: number; initializing: number }- Returns current pool statistics.
destroy(): void- Aborts all pending work, terminates all workers, and frees resources.
-
type WorkerFunction- Signature of functions that can be exposed from a worker. May return a value, a
Promise, or aWorkerResultto control transferable objects.
- Signature of functions that can be exposed from a worker. May return a value, a
-
function WorkerResult<R>(result: R, transfer: Transferable[]): WorkerResult<R>- Helper to create a
WorkerResultobject that wraps a result and a list of transferable objects.
- Helper to create a
-
type WorkerInterface<T>- Maps a plain object of
WorkerFunctions to a callable TypeScript interface used as the generic parameter ofWorkerPool.
- Maps a plain object of
-
type WorkerMethods<T>/type WorkerMethod<T, M>- Utility types that extract method names and signatures from a
WorkerInterface.
- Utility types that extract method names and signatures from a
-
interface WorkerPoolOptionsname?: string– Name of the pool (used in error messages). Default:'worker-pool'.maxWorkers?: number– Maximum number of workers in the pool. Default:HARDWARE_CONCURRENCY - 1, at least1.minIdleWorkers?: number– Minimum number of idle workers to keep. Default:0.idleTimeout?: number– Milliseconds before extra idle workers are cleaned up.0disables cleanup. Default:5000.initTimeout?: number– Milliseconds to wait for a worker to signal readiness before failing. Default:30000.creationDelay?: number– Delay before creating a new worker when the pool is already warm. Default:0.workerOptions?: WorkerOptions– Extra options passed to the underlyingWorkerconstructor (e.g.type,name).
-
function expose<T extends Record<string, WorkerFunction>>(worker: T | PromiseLike<T> | (() => T) | (() => PromiseLike<T>))- Worker-side helper that exposes an object of functions to the main thread.
- Must be called exactly once inside a worker; it automatically sets up message handling and calls
notifyReady().
-
function notifyReady(ready?: Promise<unknown>): void- Low-level worker-side API. Manually notifies the main thread that initialization has completed (successfully or with an error).
-
function waitForWorkerReady(worker: Worker, timeout?: number, signal?: AbortSignal): Promise<void>- Low-level main-thread API. Waits until the worker calls
notifyReadyor untiltimeout/signalaborts.
- Low-level main-thread API. Waits until the worker calls
MIT