async-multi-worker
Version:
Use worker as simple asynchronous function
99 lines (95 loc) • 3.84 kB
JavaScript
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;
;