multithreading
Version:
The missing standard library for multithreading in JavaScript (Works in the browser, Node.js, Deno, Bun).
97 lines (96 loc) • 3.44 kB
JavaScript
// @ts-nocheck This is a polyfill file
import { isMainThread, parentPort, Worker as NodeWorker, } from "node:worker_threads";
globalThis.self = globalThis;
// Polyfill ErrorEvent globally as it is used in both contexts
globalThis.ErrorEvent = class ErrorEvent extends Event {
constructor(type, init) {
super(type, init);
this.message = init?.message || "";
this.filename = init?.filename || "";
this.lineno = init?.lineno || 0;
this.colno = init?.colno || 0;
this.error = init?.error || null;
}
};
/**
* Main Thread Implementation
*/
if (isMainThread) {
// Directly overwrite global Worker
globalThis.Worker = class Worker extends EventTarget {
constructor(scriptURL, options) {
super();
this.onmessage = null;
this.onerror = null;
const urlStr = scriptURL.toString();
const finalPath = urlStr.startsWith("file://") ? new URL(urlStr) : urlStr;
this._worker = new NodeWorker(finalPath, { ...options });
this._worker.on("message", (data) => {
const event = new MessageEvent("message", { data });
this.dispatchEvent(event);
if (this.onmessage)
this.onmessage(event);
});
this._worker.on("error", (error) => {
const event = new ErrorEvent("error", {
error,
message: error.message,
});
this.dispatchEvent(event);
if (this.onerror)
this.onerror(event);
});
this._worker.on("exit", (code) => {
if (code !== 0) {
const err = new Error(`Worker stopped with exit code ${code}`);
const event = new ErrorEvent("error", {
error: err,
message: err.message,
});
this.dispatchEvent(event);
if (this.onerror)
this.onerror(event);
}
});
}
postMessage(message, transfer) {
this._worker.postMessage(message, transfer);
}
terminate() {
this._worker.terminate();
}
};
}
/**
* Worker Thread Implementation
*/
if (!isMainThread && parentPort) {
// We use Symbol.hasInstance so that "self instanceof WorkerGlobalScope" returns true
// even though we can't easily change the prototype of the global Node object.
class WorkerGlobalScope extends EventTarget {
static [Symbol.hasInstance](instance) {
return instance === globalThis;
}
}
globalThis.WorkerGlobalScope = WorkerGlobalScope;
// Polyfill postMessage
globalThis.postMessage = (message, transfer) => {
parentPort.postMessage(message, transfer);
};
// Polyfill onmessage
let currentHandler = globalThis.onmessage;
parentPort.on("message", (data) => {
const event = new MessageEvent("message", { data });
if (typeof globalThis.onmessage === "function") {
globalThis.onmessage(event);
}
});
Object.defineProperty(globalThis, "onmessage", {
get: () => currentHandler,
set: (fn) => {
currentHandler = fn;
},
configurable: true,
enumerable: true,
});
}