async-multi-worker
Version:
Use worker as simple asynchronous function
87 lines (83 loc) • 3.18 kB
JavaScript
'use strict';
var index = require('./node/index.cjs');
var universalWorker = require('./node/universal-worker.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 .
*
* @see func for calling worker functions
* @see terminate for cleanup
*/
class DedicatedWorker {
constructor(workerURL) {
this.calls = new Map();
this.cleanup = (error) => {
for (const { reject } of this.calls.values()) {
reject(error ?? new Error("Worker was terminated"));
}
this.calls.clear();
};
/**
* 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) => {
return (...args) => new Promise((resolve, reject) => {
const id = index.getUUID();
this.calls.set(id, { resolve, reject });
this.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.
*/
this.terminate = () => {
this.cleanup();
this.worker.terminate();
};
this.workerURL = workerURL;
this.worker = this.spawnWorker();
this.worker.onmessage = (data) => {
const { id, result, error } = data;
const call = this.calls.get(id);
if (!call)
return;
if (error) {
const e = new Error(error.message);
if (error.name)
e.name = error.name;
if (error.stack)
e.stack = error.stack;
call.reject(e);
}
else {
call.resolve(result);
}
this.calls.delete(id);
};
this.worker.onerror = this.cleanup;
this.worker.onexit = () => this.cleanup();
}
spawnWorker() {
return new universalWorker.UniversalWorker(this.workerURL);
}
get busy() {
return this.calls.size > 0;
}
}
exports.DedicatedWorker = DedicatedWorker;