worker: switch to library

Signed-off-by: Varun Patil <radialapps@gmail.com>
monorepo
Varun Patil 2023-10-30 18:00:50 -07:00
parent e1c89f9cb0
commit 9f474e3d43
5 changed files with 21 additions and 128 deletions

21
package-lock.json generated
View File

@ -30,7 +30,8 @@
"vue-router": "^3.6.5",
"vue-virtual-scroller": "1.1.2",
"vue2-leaflet": "^2.7.1",
"webdav": "^5.3.0"
"webdav": "^5.3.0",
"webworker-typed": "^1.0.4"
},
"devDependencies": {
"@playwright/test": "^1.39.0",
@ -8908,7 +8909,6 @@
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -9879,6 +9879,14 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/webworker-typed": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/webworker-typed/-/webworker-typed-1.0.4.tgz",
"integrity": "sha512-gKIJ8MkFlF44KOSs0eNPhnOUcig9vBcbzDCn0jnHZwKBpvg/p/vM3Gjruaw0fUnxnXGffrDC+aSkwTH2pIC/zg==",
"peerDependencies": {
"typescript": "^5.2.2"
}
},
"node_modules/whatwg-url": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
@ -16671,8 +16679,7 @@
"typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"dev": true
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w=="
},
"unbox-primitive": {
"version": "1.0.2",
@ -17340,6 +17347,12 @@
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
"integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w=="
},
"webworker-typed": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/webworker-typed/-/webworker-typed-1.0.4.tgz",
"integrity": "sha512-gKIJ8MkFlF44KOSs0eNPhnOUcig9vBcbzDCn0jnHZwKBpvg/p/vM3Gjruaw0fUnxnXGffrDC+aSkwTH2pIC/zg==",
"requires": {}
},
"whatwg-url": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",

View File

@ -51,7 +51,8 @@
"vue-router": "^3.6.5",
"vue-virtual-scroller": "1.1.2",
"vue2-leaflet": "^2.7.1",
"webdav": "^5.3.0"
"webdav": "^5.3.0",
"webworker-typed": "^1.0.4"
},
"engines": {
"node": ">=18.2.0",

View File

@ -1,6 +1,6 @@
import { API } from '@services/API';
import { onDOMLoaded } from '@services/utils';
import { importWorker } from '@services/worker';
import { importWorker } from 'webworker-typed';
import type XImgWorker from './XImgWorker';
// Global web worker to fetch images

View File

@ -1,5 +1,5 @@
import { CacheExpiration } from 'workbox-expiration';
import { exportWorker } from '@services/worker';
import { exportWorker } from 'webworker-typed';
declare var self: ServiceWorkerGlobalScope;

View File

@ -1,121 +0,0 @@
/**
* Data sent from main thread to worker.
*/
type CommRequest = {
reqid: number;
name: string;
args: any[];
};
/**
* Data sent from worker to main thread.
*/
type CommResult = {
reqid: number;
resolve?: any;
reject?: string;
};
/**
* Map of function names to functions.
*/
type FunctionMap = { [name: string]: Function };
/**
* Utility type to convert all methods in an object to async.
*/
type Async<T extends FunctionMap> = {
[K in keyof T]: T[K] extends (...args: infer A) => Promise<infer R>
? (...args: A) => Promise<R>
: T[K] extends (...args: infer A) => infer R
? (...args: A) => Promise<R>
: T[K];
};
/**
* Export methods from a worker to the main thread.
*
* @param handlers Object with methods to export
*
* @example
* ```ts
* // my-worker.ts
* function foo() { return 'bar'; }
*
* async function asyncFoo() { return 'bar'; }
*
* export default exportWorker({
* foo,
* asyncFoo,
* inline: () => 'bar',
* });
*/
export function exportWorker<T extends FunctionMap>(handlers: T): Async<T> {
self.onmessage = async ({ data }: { data: CommRequest }) => {
try {
// Get handler from registrations
const handler = handlers[data.name];
if (!handler) throw new Error(`[BUG] No handler for type ${data.name}`);
// Run handler
let result = handler.apply(self, data.args);
if (result instanceof Promise) {
result = await result;
}
// Success - post back to main thread
self.postMessage({ reqid: data.reqid, resolve: result } as CommResult);
} catch (e) {
// Error - post back rejection
self.postMessage({ reqid: data.reqid, reject: e.message } as CommResult);
}
};
return null as unknown as Async<T>;
}
/**
* Import a worker exported with `exportWorker`.
*
* @param worker Worker to import
*
* @example
* ```ts
* // main.ts
* import type MyWorker from './my-worker.ts';
*
* const worker = importWorker<typeof MyWorker>(new Worker(new URL('./XImgWorkerStub.ts', import.meta.url)));
*
* async (() => {
* // all methods are async
* console.assert(await worker.foo() === 'bar');
* console.assert(await worker.asyncFoo() === 'bar');
* console.assert(await worker.inline() === 'bar');
* });
*/
export function importWorker<T>(worker: Worker) {
const promises = new Map<number, { resolve: Function; reject: Function }>();
// Handle messages from worker
worker.onmessage = ({ data }: { data: CommResult }) => {
const { reqid, resolve, reject } = data;
if (resolve) promises.get(reqid)?.resolve(resolve);
if (reject) promises.get(reqid)?.reject(reject);
promises.delete(reqid);
};
// Create proxy to call worker methods
const proxy = new Proxy(worker, {
get(target: Worker, name: string) {
return async function wrapper(...args: any[]) {
return await new Promise((resolve, reject) => {
const reqid = Math.random();
promises.set(reqid, { resolve, reject });
target.postMessage({ reqid, name, args } as CommRequest);
});
};
},
});
return proxy as T;
}