worktank
Version:
A simple isomorphic library for executing functions inside WebWorkers or Node Threads pools.
109 lines (108 loc) • 3.52 kB
JavaScript
/* IMPORT */
import WorkerError from './error.js';
import WorkerFrontend from './frontend.js';
/* MAIN */
class Worker {
/* CONSTRUCTOR */
constructor(name, bootloader) {
/* EVENTS API */
this.onClose = (code) => {
if (this.terminated)
return;
this.terminated = true;
this.worker.terminate();
this.reject(new WorkerError(this.name, `Exited with exit code ${code}`));
};
this.onError = (error) => {
if (this.terminated)
return;
this.terminated = true;
this.worker.terminate();
this.reject(error);
};
this.onMessage = (message) => {
if (this.terminated)
return;
if (message.type === 'log') {
this.onMessageLog(message);
}
else if (message.type === 'ready') {
this.onMessageReady(message);
}
else if (message.type === 'result') {
this.onMessageResult(message);
}
};
this.onMessageLog = (message) => {
console.log(message.value);
};
this.onMessageReady = (message) => {
this.ready = true;
this.tick();
};
this.onMessageResult = (message) => {
if ('value' in message) { // Success
this.resolve(message.value);
}
else { // Error
const error = Object.assign(new Error(), message.error);
this.reject(error);
}
};
/* API */
this.exec = (task) => {
if (this.terminated)
throw new WorkerError(this.name, 'Terminated');
if (this.task || this.busy)
throw new WorkerError(this.name, 'Busy');
this.task = task;
this.tick();
};
this.reject = (error) => {
const { task } = this;
if (!task)
return;
this.busy = false;
this.task = undefined;
this.timestamp = Date.now();
task.reject(error);
};
this.resolve = (value) => {
const { task } = this;
if (!task)
return;
this.busy = false;
this.task = undefined;
this.timestamp = Date.now();
task.resolve(value);
};
this.terminate = () => {
if (this.terminated)
return;
this.terminated = true;
this.worker.terminate();
this.reject(new WorkerError(this.name, 'Terminated'));
};
this.tick = () => {
if (this.terminated || !this.ready || !this.task || this.busy)
return;
this.busy = true;
try {
const { method, args, transfer } = this.task;
this.worker.send({ type: 'exec', method, args }, transfer);
}
catch {
this.reject(new WorkerError(this.name, 'Failed to send message'));
}
};
this.busy = false;
this.ready = false;
this.terminated = false;
this.timestamp = Date.now();
this.name = name;
this.bootloader = bootloader;
this.worker = new WorkerFrontend(this.name, this.bootloader, this.onClose, this.onError, this.onMessage);
}
}
/* EXPORT */
export default Worker;