Replies: 3 comments 3 replies
-
|
Something like |
Beta Was this translation helpful? Give feedback.
-
|
@marvinhagemeister Wow, I'm embarrassed to say I didn't know such a thing existed. In my imagination, for example, I'd provide functions on the child thread and parent thread side that wrap message passing. Both functions would allow you to specify the argument and return type. As for the child thread, I'd like to reuse it once it's started, so I'd assign an ID to the raw messages. I'll also limit each file to one callback. This is actual sample code. Proposal: worker_stdlib.ts/// <reference lib="deno.worker" />
interface WorkerMessage<T> {
id: string;
body: T;
}
interface WorkerResult<T> {
message: T,
transferables?: Transferable[]
}
export function workerChild<T, U>(callback: (message: T, event: MessageEvent<WorkerMessage<T>>) => WorkerResult<U> | Promise<WorkerResult<U>>) {
globalThis.addEventListener("message", async (event: MessageEvent<WorkerMessage<T>>) => {
try {
const result = await callback(event.data.body, event);
globalThis.postMessage({
id: event.data.id,
body: result.message
} satisfies WorkerMessage<U>, {
transfer: result?.transferables
});
} catch (error) {
throw `${(error as object).toString()};${event.data.id};`;
}
});
}
export function workerParent<T, U>(worker: Worker) {
return {
exit() {
worker.terminate();
},
run(message: T, transferables?: Transferable[]): Promise<U> {
return new Promise<U>((res, rej) => {
// It looks like it will be stable soon. However, there are many other ways to generate ID, so you will need to consider various methods.
// @ts-ignore enable-v8-flags
const id = crypto.getRandomValues(new Uint8Array(16)).toHex();
function onMessage(event: MessageEvent<WorkerMessage<U>>): void {
if (event.data.id === id) {
worker.removeEventListener("message", onMessage);
worker.removeEventListener("error", onError);
res(event.data.body);
}
}
function onError(event: ErrorEvent): void {
if (event.message.endsWith(`;${id};`)) {
worker.removeEventListener("message", onMessage);
worker.removeEventListener("error", onError);
rej(event);
}
}
worker.addEventListener("message", onMessage);
worker.addEventListener("error", onError);
worker.postMessage({
id: id,
body: message
} satisfies WorkerMessage<T>, {
transfer: transferables
});
});
}
};
}Usage example: worker_parent.tsimport {workerParent} from "./worker_stdlib.ts";
const worker = new Worker(new URL("./worker_child.ts", import.meta.url), {
type: "module"
});
const {exit, run} = workerParent<number, ArrayBuffer>(worker);
await Promise.all([
run(2).then(res => console.log("Result 1: ", res)),
run(4).then(res => console.log("Result 2: ", res))
]);
exit();Usage example: worker_child.tsimport {workerChild} from "./worker_stdlib.ts";
workerChild<number, ArrayBuffer>((n) => {
const random = new Float32Array(1024 ** 2 * n / 4).map(() => Math.random()).buffer;
return {
message: random,
transferables: [random]
}
}); |
Beta Was this translation helpful? Give feedback.
-
|
I understood that ideas like mine already exist. The remaining question is, does it make sense to add this to the standard library as a trusted first-party library? Of course, as the originator, I think it makes sense, and I would actively use it if it were added. I think the title of "standard library" is a big advantage. However, since it's a standard library, there will be aspects that we need to consider, such as the operational costs of regular maintenance. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I thought it would be useful to have a library that makes it easy to introduce multi-threading using WebWorker into a project.
NodeJS's
worker_threads, for example, is easy to use, but is not compatible with browsers.I think the advantage of Deno's stdlib is that it's "universal" and can be run regardless of the execution environment, so I think it would be a good idea to use
Worker, which is available in all environments.It's well known that WebWorker uses message passing via callbacks such as onmessage, which makes it a little difficult to use from the perspective of modern programming that makes heavy use of Async/Await.
In fact, when I incorporate WebWorker into my projects, I create my own simple Promise wrapper class.
If worker were added to the reliable stdlib, anyone could easily enjoy the benefits of multi-threading, with greater peace of mind than using home-grown or third-party methods.
However, as I mentioned earlier, through creating my own wrapper, even though it is simple, I have come to understand the difficulties of design.
For example, even if you convert to a Promise, there is no problem on the parent thread side because it is wrapped, but the child thread side becomes a separate module, so the user has to implement it themselves.
Forcing users to use library-derived syntax would, in a sense, be an exaggeration, almost like a framework.
In my case, since it was a small project, I took a somewhat forceful approach: I took "inline" worker code written in template literals and implemented the processing in
main(), and then combined it withglobalThis.onmessage ~ postMessage(main()), which returns data to the parent thread, converted it into a DataURL, and had theWorkerload it.However, I think there are more suitable and elegant ways to create a library.
Please let us know your thoughts.
Beta Was this translation helpful? Give feedback.
All reactions