UNPKG

async-multi-worker

Version:
99 lines (95 loc) 3.84 kB
'use strict'; var index = require('./node/index.cjs'); var errors = require('./errors.cjs'); var workerManager = require('./worker-manager.cjs'); /* eslint-disable @typescript-eslint/no-explicit-any */ /** * A generic handler for making asynchronous function calls to a Worker. * * This class manages communication between the main thread and a worker, allowing you to call worker-exposed functions as Promises. * It handles message passing, result/error propagation, timeouts, and worker cleanup. * * @template T - The type describing the functions exposed by the worker (should extend FunctionsRecord). * * @see func for calling worker functions * @see terminate for cleanup */ class ElasticWorker { constructor(workerURL, maxIdleWorkers) { /** * Returns a function that calls a method in the worker asynchronously with optional timeout. * * @template K - The key of the function in the worker object. * @param funcName - The name of the function to call in the worker. * @param timeoutMs - Optional timeout in milliseconds (default: 5000ms). * @returns A function that, when called with arguments, returns a Promise resolving to the result of the worker function. * * @example * const add = workerProxy.func('add'); * const result = await add(1, 2); */ this.func = (funcName, timeoutMs = 5000) => { return (...args) => new Promise((resolve, reject) => { const id = index.getUUID(); const worker = this.workerManager.getWorker(); let timeoutId; if (timeoutMs && timeoutMs !== Infinity && timeoutMs > 0) { timeoutId = setTimeout(() => { reject(new errors.TimeoutError(timeoutMs)); this.workerManager.terminateWorker(worker); }, timeoutMs); } worker.onmessage = this.messageListener({ worker, resolve, reject, timeoutId, }); worker.onerror = this.onWorkerError({ reject, worker }); worker.onexit = this.onWorkerExit(reject); worker.postMessage({ func: funcName, args, id }); }); }; /** * Terminates the worker and cleans up all pending calls. * This method removes all event listeners and clears the calls map. * It should be called when the worker is no longer needed to prevent memory leaks. * * ! Keep in mind that this will stop all workers including the workers with ongoing calls. */ this.terminate = () => { this.workerManager.terminateAllWorkers(); }; this.workerManager = new workerManager.WorkerManager(workerURL, maxIdleWorkers); } messageListener({ worker, resolve, reject, timeoutId, }) { return (data) => { const { result, error } = data; if (error) { const e = new Error(error.message); if (error.name) e.name = error.name; if (error.stack) e.stack = error.stack; reject(e); } else { resolve(result); } clearTimeout(timeoutId); this.workerManager.idleWorker(worker); }; } onWorkerExit(reject) { return () => { reject(new Error("Worker was terminated")); }; } onWorkerError({ reject, worker, }) { return (error) => { reject(error); this.workerManager.terminateWorker(worker); }; } } exports.ElasticWorker = ElasticWorker;