diff --git a/web/database/utils/WorkerConnectionProxy.js b/web/database/utils/WorkerConnectionProxy.js new file mode 100644 --- /dev/null +++ b/web/database/utils/WorkerConnectionProxy.js @@ -0,0 +1,69 @@ +// @flow + +import type { + WorkerRequestProxyMessage, + WorkerResponseMessage, + WorkerRequestMessage, + WorkerResponseProxyMessage, +} from '../../types/worker-types.js'; + +type PromiseCallbacks = { + +resolve: (result: WorkerResponseMessage) => void, + +reject: (error: Error) => void, +}; + +// It's important to have only ONE instance of this object to make it work +class WorkerConnectionProxy { + workerPort: MessagePort; + messageId: number; + promiseCallbacks: { [index: number]: PromiseCallbacks }; + // This function will be called when worker will send + // a message with an error but without any app's request + onError: (error: Error) => void; + + constructor(port: MessagePort, onError: (error: Error) => void) { + this.workerPort = port; + this.onError = onError; + this.workerPort.onmessage = this.handleMessage; + this.messageId = 1; + this.promiseCallbacks = {}; + } + + handleMessage: (msg: MessageEvent) => void = (msg: MessageEvent) => { + const data: WorkerResponseProxyMessage = (msg.data: any); + const { id, error, message } = data; + + const callbacks = this.promiseCallbacks[id]; + + if (!callbacks && !error) { + this.onError(new Error('Unknown error')); + } else if (!callbacks && error) { + this.onError(error); + } else if (error) { + callbacks.reject(error); + } else { + callbacks.resolve(message); + } + + delete this.promiseCallbacks[id]; + }; + + scheduleOnWorker( + payload: WorkerRequestMessage, + ): Promise { + const msgId = this.messageId++; + const msg: WorkerRequestProxyMessage = { + id: msgId, + message: payload, + }; + return new Promise((resolve, reject) => { + this.promiseCallbacks[msgId] = { + resolve, + reject, + }; + this.workerPort.postMessage(msg); + }); + } +} + +export default WorkerConnectionProxy; diff --git a/web/database/worker/web-db-worker.js b/web/database/worker/web-db-worker.js --- a/web/database/worker/web-db-worker.js +++ b/web/database/worker/web-db-worker.js @@ -8,6 +8,7 @@ type WorkerResponseMessage, workerRequestMessageTypes, workerResponseMessageTypes, + type WorkerRequestProxyMessage, } from '../../types/worker-types.js'; const localforageConfig: PartialConfig = { @@ -37,13 +38,24 @@ console.log('Web database worker alive!'); port.onmessage = async function (messageEvent: MessageEvent) { - const message: WorkerRequestMessage = (messageEvent.data: any); + const data: WorkerRequestProxyMessage = (messageEvent.data: any); + const { id, message } = data; + + if (!id) { + port.postMessage({ + error: new Error('Request without identifier'), + }); + } try { const result = processAppRequest(message); - port.postMessage(result); + port.postMessage({ + id, + message: result, + }); } catch (e) { port.postMessage({ + id, error: e, }); } diff --git a/web/types/worker-types.js b/web/types/worker-types.js --- a/web/types/worker-types.js +++ b/web/types/worker-types.js @@ -12,22 +12,28 @@ export type WorkerRequestMessage = PingWorkerRequestMessage; +export type WorkerRequestProxyMessage = { + +id: number, + +message: WorkerRequestMessage, +}; + // The types of messages sent from worker to app export const workerResponseMessageTypes = Object.freeze({ PONG: 0, }); -export type ErrorWorkerResponseMessage = { - +error: Error, -}; export type PongWorkerResponseMessage = { +type: 0, +text: string, }; -export type WorkerResponseMessage = - | PongWorkerResponseMessage - | ErrorWorkerResponseMessage; +export type WorkerResponseMessage = PongWorkerResponseMessage; + +export type WorkerResponseProxyMessage = { + +id: number, + +message: WorkerResponseMessage, + +error?: Error, +}; // SharedWorker types export type SharedWorkerMessageEvent = MessageEvent & {