Skip to content

Instantly share code, notes, and snippets.

@steida
Created February 10, 2022 23:27
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save steida/81c207163c24179065245a9e3882de93 to your computer and use it in GitHub Desktop.
Save steida/81c207163c24179065245a9e3882de93 to your computer and use it in GitHub Desktop.
Typed Web Worker
/**
* Typed Web Worker.
*
* type InputData = { name: string };
* type OutputData = { nameLength: number }
*
* // main.js
* const worker = createTypedWorker<InputData, OutputData>(worker);
*
* // worker.js
* const worker = createTypedWorker<OutputData, InputData>();
*/
interface Subscription {
remove: () => void;
}
export interface TypedWorker<InputData, OutputData> {
postMessage: (data: InputData) => void;
// Ready for useSyncExternalStore.
// https://blog.saeloun.com/2021/12/30/react-18-usesyncexternalstore-api
onMessage: (listener: () => void) => Subscription;
getMessage: () => OutputData | null;
dispose: () => void;
}
export const createTypedWorker = <InputData, OutputData>(
worker: Worker = self as unknown as Worker
): TypedWorker<InputData, OutputData> => {
const listeners = new Set<() => void>();
let data: OutputData | null = null;
const handleMessage = (e: MessageEvent) => {
if (e.data && e.data.type === "__typedWorker") {
data = e.data.data;
listeners.forEach((l) => l());
}
};
worker.addEventListener("message", handleMessage);
return {
postMessage(data) {
worker.postMessage({ type: "__typedWorker", data });
},
onMessage(listener) {
listeners.add(listener);
return {
remove: () => {
listeners.delete(listener);
},
};
},
getMessage() {
return data;
},
dispose() {
worker.removeEventListener("message", handleMessage);
},
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment