UNPKG

async-multi-worker

Version:
144 lines (140 loc) 5 kB
'use strict'; require('worker_threads'); var universalWorker = require('./node/universal-worker.cjs'); require('crypto'); /** * Manages a pool of Workers, optimizing resource usage by limiting the number of non-busy workers. * Provides methods to spawn, reuse, terminate, and clean up workers. * * ## Usage Example * * ```typescript * // 1. Instantiate the manager with the worker script URL * const manager = new WorkerManager(new URL('./worker.js', import.meta.url)); * * // 2. Get an available worker (or spawn a new one) * const worker = manager.getWorker(); * * // 3. Mark the worker busy/free as needed * // worker.busy = true; // when starting a task * // worker.busy = false; // when done * * // 4. Terminate a specific worker * manager.terminateWorker(worker); * * // 5. Cleanup all workers when done * manager.terminateAllWorkers(); * ``` * */ class WorkerManager { /** * Creates a WorkerManager instance. * @param workerURL URL of the worker script. * @param maxIdleWorkers Maximum number of non-busy workers to keep alive (default: 5). */ constructor(workerURL, maxIdleWorkers = 5) { this.workers = new Map(); /** * Spawns a new Worker and adds it to the pool. * @returns The newly created WorkerInfo instance. */ this.spawnWorker = () => { const worker = new universalWorker.UniversalWorker(this.workerURL); const workerInfo = new WorkerInfo(worker); this.workers.set(worker, workerInfo); return workerInfo; }; /** * Retrieves an available non-busy Worker from the pool, or spawns a new one if none are available. * Also removes excess non-busy workers. * @returns An available WorkerInfo instance. */ this.getWorker = () => { for (const workerInfo of this.workers.values()) { if (!workerInfo.busy) { workerInfo.busy = true; return workerInfo.worker; } } const workerInfo = this.spawnWorker(); workerInfo.busy = true; return workerInfo.worker; }; /** * * @deprecated invoke idleWorker when the process is done * * Removes excess idle workers from the pool, keeping only up to MAX_IDLE_WORKERS. */ this.removeIdleWorkers = () => { const idleWorkers = []; for (const workerInfo of this.workers.values()) { if (!workerInfo.busy) idleWorkers.push(workerInfo); } if (idleWorkers.length <= this.MAX_IDLE_WORKERS) return; const excessWorkers = idleWorkers.slice(this.MAX_IDLE_WORKERS); for (const workerInfo of excessWorkers) { this.workers.delete(workerInfo.worker); workerInfo.worker.terminate(); } }; /** * Terminates a specific Worker and removes it from the pool. * @param worker The Worker instance to terminate. */ this.terminateWorker = (worker) => { const workerInfo = this.workers.get(worker); if (workerInfo) this.workers.delete(worker); worker.terminate(); }; /** * Terminates a specific Worker and removes it from the pool when it is idle and existing workers exceed MAX_IDLE_WORKERS . * @param worker The Worker instance to terminate. */ this.idleWorker = (worker) => { const workerInfo = this.workers.get(worker); if (!workerInfo) return worker.terminate(); workerInfo.busy = false; this.eliminateIfExceedingMaxIdleWorkers(workerInfo); }; this.eliminateIfExceedingMaxIdleWorkers = (workerInfo) => { let count = 0; for (const info of this.workers.values()) { if (!info.busy) count++; if (count > this.MAX_IDLE_WORKERS) { this.workers.delete(workerInfo.worker); workerInfo.worker.terminate(); break; } } }; /** * Terminates all workers and clears the pool. * * ! be cautious when using this method, as it will stop all ongoing tasks. */ this.terminateAllWorkers = () => { for (const workerInfo of this.workers.values()) { workerInfo.worker.terminate(); } this.workers.clear(); }; this.workerURL = workerURL; this.MAX_IDLE_WORKERS = maxIdleWorkers; this.spawnWorker(); } } class WorkerInfo { constructor(worker) { this.worker = worker; this.busy = false; } } exports.WorkerInfo = WorkerInfo; exports.WorkerManager = WorkerManager;