diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 97cefe8..8641b2c 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -13,7 +13,11 @@ jobs: - name: Use Node.js uses: actions/setup-node@v3 with: - node-version: '16' + node-version: '18' + - name: Lint + shell: bash + run: | + npm run lint - name: Build shell: bash run: | @@ -27,7 +31,3 @@ jobs: shell: bash run: | npm --prefix packages/examples run build:production - - name: Lint - shell: bash - run: | - npm run lint diff --git a/packages/examples/src/com/WorkerCom.ts b/packages/examples/src/com/WorkerCom.ts index b92a5d4..3b17038 100644 --- a/packages/examples/src/com/WorkerCom.ts +++ b/packages/examples/src/com/WorkerCom.ts @@ -4,45 +4,86 @@ import { WorkerTaskMessage, WorkerTaskMessageType } from 'wtd-core'; +import { updateText } from '../worker/ComWorkerCommon.js'; /** * Hello World example using a classic worker */ class HelloWorldStandardWorkerExample { - async run() { - const workerTaskCom1 = new WorkerTask('Com1Worker', 1, { + private workerTaskCom1: WorkerTask; + private workerTaskCom2: WorkerTask; + private canvasMain: HTMLCanvasElement; + private canvasCom1: HTMLCanvasElement; + private canvasCom2: HTMLCanvasElement; + + constructor() { + this.canvasMain = document.getElementById('main') as HTMLCanvasElement; + this.canvasCom1 = document.getElementById('com1') as HTMLCanvasElement; + this.canvasCom2 = document.getElementById('com2') as HTMLCanvasElement; + + this.recalAspectRatio(this.canvasMain); + this.recalAspectRatio(this.canvasCom1); + this.recalAspectRatio(this.canvasCom2); + + updateText({ + text: 'Main: Init', + width: this.canvasMain?.clientWidth ?? 0, + height: this.canvasMain?.clientHeight ?? 0, + canvas: this.canvasMain, + log: true + }); + + this.workerTaskCom1 = new WorkerTask('Com1Worker', 1, { module: true, blob: false, url: new URL(import.meta.env.DEV ? '../worker/Com1Worker.ts' : '../worker/generated/Com1Worker-es.js', import.meta.url) }); - const workerTaskCom2 = new WorkerTask('Com2Worker', 1, { + this.workerTaskCom2 = new WorkerTask('Com2Worker', 1, { module: true, blob: false, url: new URL(import.meta.env.DEV ? '../worker/Com2Worker.ts' : '../worker/generated/Com2Worker-es.js', import.meta.url) }); + } - try { - const channel = new MessageChannel(); + recalAspectRatio(canvas: HTMLCanvasElement) { + canvas.width = canvas.height * (canvas.clientWidth / canvas.clientHeight); + } + + async run() { + const channel = new MessageChannel(); + const initCom1 = new WorkerTaskMessage(); + const payload1 = new RawPayload(); - const initCom1 = new WorkerTaskMessage(); - const payload1 = new RawPayload(); - payload1.message.raw = { port: channel.port1 }; + try { + const offscreenCom1 = this.canvasCom1.transferControlToOffscreen(); + payload1.message.raw = { + port: channel.port1, + drawingSurface: offscreenCom1, + width: this.canvasCom1.clientWidth, + height: this.canvasCom1.clientHeight + }; initCom1.addPayload(payload1); const initCom2 = new WorkerTaskMessage(); const payload2 = new RawPayload(); - payload2.message.raw = { port: channel.port2 }; + const offscreenCom2 = this.canvasCom2.transferControlToOffscreen(); + payload2.message.raw = { + port: channel.port2, + drawingSurface: offscreenCom2, + width: this.canvasCom2.clientWidth, + height: this.canvasCom2.clientHeight + }; initCom2.addPayload(payload2); const promisesinit = []; - promisesinit.push(workerTaskCom1.initWorker({ + promisesinit.push(this.workerTaskCom1.initWorker({ message: initCom1, - transferables: [channel.port1] + transferables: [channel.port1, offscreenCom1] })); - promisesinit.push(workerTaskCom2.initWorker({ + promisesinit.push(this.workerTaskCom2.initWorker({ message: initCom2, - transferables: [channel.port2], + transferables: [channel.port2, offscreenCom2], })); await Promise.all(promisesinit); @@ -54,24 +95,31 @@ class HelloWorldStandardWorkerExample { console.log(`Worker said onComplete: ${rawPayload.message.raw.finished}`); }; - const promisesExec = []; - promisesExec.push(workerTaskCom1.executeWorker({ - message: new WorkerTaskMessage(), - onComplete - })); - promisesExec.push(workerTaskCom2.executeWorker({ - message: new WorkerTaskMessage(), - onComplete - })); + const promisesExec: Array> = []; + setTimeout(async () => { + promisesExec.push(this.workerTaskCom1.executeWorker({ + message: new WorkerTaskMessage(), + onComplete + })); + promisesExec.push(this.workerTaskCom2.executeWorker({ + message: new WorkerTaskMessage(), + onComplete + })); - const result = await Promise.all(promisesExec); - console.log(`Overall result: ${result}`); + const result = await Promise.all(promisesExec); + console.log(`Overall result: ${result}`); - const t1 = performance.now(); - const msg = `Worker execution has been completed after ${t1 - t0}ms.`; - console.log(msg); - alert(msg); - console.log('Done'); + const t1 = performance.now(); + const text = `Main: Worker execution has been completed after ${t1 - t0}ms.`; + updateText({ + text, + width: this.canvasMain?.clientWidth ?? 0, + height: this.canvasMain?.clientHeight ?? 0, + canvas: this.canvasMain, + log: true + }); + console.log('Done'); + }, 2000); } catch (e) { console.error(e); } @@ -79,4 +127,4 @@ class HelloWorldStandardWorkerExample { } const app = new HelloWorldStandardWorkerExample(); -app.run(); +await app.run(); diff --git a/packages/examples/src/worker/Com1Worker.ts b/packages/examples/src/worker/Com1Worker.ts index 7b09271..39f9ef7 100644 --- a/packages/examples/src/worker/Com1Worker.ts +++ b/packages/examples/src/worker/Com1Worker.ts @@ -9,19 +9,30 @@ import { WorkerTaskMessageType, WorkerTaskWorker } from 'wtd-core'; +import { getOffScreenCanvas, updateText } from './ComWorkerCommon.js'; export class Com1Worker implements WorkerTaskWorker, InterComWorker { private icph = new InterComPortHandler(); + private offScreenCanvas?: HTMLCanvasElement; init(message: WorkerTaskMessageType): void { // register the default com-routing function for inter-worker communication - this.icph.registerPort('com2', message.payloads[0], message => comRouting(this, message)); + const payload = message.payloads[0]; + this.icph.registerPort('com2', payload, message => comRouting(this, message)); + this.offScreenCanvas = getOffScreenCanvas(payload); + updateText({ + text: 'Worker 1: init', + width: this.offScreenCanvas?.width ?? 0, + height: this.offScreenCanvas?.height ?? 0, + canvas: this.offScreenCanvas, + log: true + }); // send initComplete to main const initComplete = WorkerTaskMessage.createFromExisting({} as WorkerTaskMessageType, WorkerTaskCommandResponse.INIT_COMPLETE); - const payload = new RawPayload({ hello: 'Worker 1 initComplete!' }); - initComplete.addPayload(payload); + const payloadResponse = new RawPayload({ hello: 'Worker 1 initComplete!' }); + initComplete.addPayload(payloadResponse); self.postMessage(initComplete); } @@ -37,19 +48,36 @@ export class Com1Worker implements WorkerTaskWorker, InterComWorker { interComIntermediate(message: WorkerTaskMessageType): void { const rawPayload = message.payloads[0] as RawPayload; - console.log(`Worker 2 said: ${rawPayload.message.raw.hello}`); + const text = `Worker 1: Worker 2 said: ${rawPayload.message.raw.hello}`; + updateText({ + text, + width: this.offScreenCanvas?.width ?? 0, + height: this.offScreenCanvas?.height ?? 0, + canvas: this.offScreenCanvas, + log: true + }); - // after receiving the message from Com2Worker, send interComIntermediateConfirm to worker 2 - const intermediateConfirm = WorkerTaskMessage.createFromExisting(message, WorkerTaskCommandResponse.INTERCOM_INTERMEDIATE_CONFIRM); - const payload = new RawPayload({ confirmed: 'Hi Worker 2. I confirm!' }); - intermediateConfirm.addPayload(payload); + setTimeout(() => { + // after receiving the message from Com2Worker, send interComIntermediateConfirm to worker 2 + const intermediateConfirm = WorkerTaskMessage.createFromExisting(message, WorkerTaskCommandResponse.INTERCOM_INTERMEDIATE_CONFIRM); + const payload = new RawPayload({ confirmed: 'Hi Worker 2. I confirm!' }); + intermediateConfirm.addPayload(payload); - this.icph.postMessageOnPort('com2', intermediateConfirm); + this.icph.postMessageOnPort('com2', intermediateConfirm); + }, 2000); } interComIntermediateConfirm(message: WorkerTaskMessageType): void { const rawPayload = message.payloads[0] as RawPayload; - console.log(`Worker 2 confirmed: ${rawPayload.message.raw.confirmed}`); + const text = `Worker 1: Worker 2 confirmed: ${rawPayload.message.raw.confirmed}`; + + updateText({ + text, + width: this.offScreenCanvas?.width ?? 0, + height: this.offScreenCanvas?.height ?? 0, + canvas: this.offScreenCanvas, + log: true + }); // after receiving the interComIntermediateConfirm from Com2Worker, send execComplete to main const execComplete = WorkerTaskMessage.createFromExisting(message, WorkerTaskCommandResponse.EXECUTE_COMPLETE); @@ -57,7 +85,6 @@ export class Com1Worker implements WorkerTaskWorker, InterComWorker { execComplete.addPayload(payload); self.postMessage(execComplete); } - } const worker = new Com1Worker(); diff --git a/packages/examples/src/worker/Com2Worker.ts b/packages/examples/src/worker/Com2Worker.ts index e2619b8..144cc91 100644 --- a/packages/examples/src/worker/Com2Worker.ts +++ b/packages/examples/src/worker/Com2Worker.ts @@ -9,19 +9,30 @@ import { WorkerTaskMessageType, WorkerTaskWorker } from 'wtd-core'; +import { getOffScreenCanvas, updateText } from './ComWorkerCommon.js'; export class Com2Worker implements WorkerTaskWorker, InterComWorker { private icph = new InterComPortHandler(); + private offScreenCanvas?: HTMLCanvasElement; init(message: WorkerTaskMessageType): void { // register the default com-routing function for inter-worker communication - this.icph.registerPort('com1', message.payloads[0], message => comRouting(this, message)); + const payload = message.payloads[0]; + this.icph.registerPort('com1', payload, message => comRouting(this, message)); + this.offScreenCanvas = getOffScreenCanvas(payload); + updateText({ + text: 'Worker 2: init', + width: this.offScreenCanvas?.width ?? 0, + height: this.offScreenCanvas?.height ?? 0, + canvas: this.offScreenCanvas, + log: true + }); // send initComplete to main const initComplete = WorkerTaskMessage.createFromExisting({} as WorkerTaskMessageType, WorkerTaskCommandResponse.INIT_COMPLETE); - const payload = new RawPayload({ hello: 'Worker 2 initComplete!' }); - initComplete.addPayload(payload); + const payloadResponse = new RawPayload({ hello: 'Worker 2 initComplete!' }); + initComplete.addPayload(payloadResponse); self.postMessage(initComplete); } @@ -37,19 +48,35 @@ export class Com2Worker implements WorkerTaskWorker, InterComWorker { interComIntermediate(message: WorkerTaskMessageType): void { const rawPayload = message.payloads[0] as RawPayload; - console.log(`Worker 1 said: ${rawPayload.message.raw.hello}`); + const text = `Worker 2: Worker 1 said: ${rawPayload.message.raw.hello}`; + updateText({ + text, + width: this.offScreenCanvas?.width ?? 0, + height: this.offScreenCanvas?.height ?? 0, + canvas: this.offScreenCanvas, + log: true + }); - // after receiving the message from Com1Worker, send interComIntermediateConfirm to worker 2 - const intermediateConfirm = WorkerTaskMessage.createFromExisting(message, WorkerTaskCommandResponse.INTERCOM_INTERMEDIATE_CONFIRM); - const payload = new RawPayload({ confirmed: 'Hi Worker 1. I confirm!' }); - intermediateConfirm.addPayload(payload); + setTimeout(() => { + // after receiving the message from Com1Worker, send interComIntermediateConfirm to worker 2 + const intermediateConfirm = WorkerTaskMessage.createFromExisting(message, WorkerTaskCommandResponse.INTERCOM_INTERMEDIATE_CONFIRM); + const payload = new RawPayload({ confirmed: 'Hi Worker 1. I confirm!' }); + intermediateConfirm.addPayload(payload); - this.icph.postMessageOnPort('com1', intermediateConfirm); + this.icph.postMessageOnPort('com1', intermediateConfirm); + }, 2000); } interComIntermediateConfirm(message: WorkerTaskMessageType): void { const rawPayload = message.payloads[0] as RawPayload; - console.log(`Worker 1 confirmed: ${rawPayload.message.raw.confirmed}`); + const text = `Worker 2: Worker 1 confirmed: ${rawPayload.message.raw.confirmed}`; + updateText({ + text, + width: this.offScreenCanvas?.width ?? 0, + height: this.offScreenCanvas?.height ?? 0, + canvas: this.offScreenCanvas, + log: true + }); // after receiving the interComIntermediateConfirm from Com1Worker, send execComplete to main const execComplete = WorkerTaskMessage.createFromExisting(message, WorkerTaskCommandResponse.EXECUTE_COMPLETE); diff --git a/packages/examples/src/worker/ComWorkerCommon.ts b/packages/examples/src/worker/ComWorkerCommon.ts new file mode 100644 index 0000000..10c22db --- /dev/null +++ b/packages/examples/src/worker/ComWorkerCommon.ts @@ -0,0 +1,23 @@ +import { Payload, RawPayload } from 'wtd-core'; + +export const getOffScreenCanvas = (payload: Payload) => { + return payload ? (payload as RawPayload).message.raw.drawingSurface as HTMLCanvasElement : undefined; +}; + +export const updateText = (params: { + text: string; + width: number; + height: number; + canvas?: HTMLCanvasElement; + log?: boolean; +}) => { + const context = params.canvas?.getContext('2d'); + if (context) { + context.clearRect(0, 0, params.width, params.height); + context.font = '12px Arial'; + context?.fillText(params.text, 12, 48); + } + if (params.log === true) { + console.log(params.text); + } +}; diff --git a/packages/examples/vite.config.production.ts b/packages/examples/vite.config.production.ts index 48780cf..74821ae 100644 --- a/packages/examples/vite.config.production.ts +++ b/packages/examples/vite.config.production.ts @@ -9,7 +9,7 @@ export default defineConfig(({ command }) => { console.log(`Running: ${command}`); return { build: { - target: ['es2020'], + target: ['es2022'], rollupOptions: { input: { main: path.resolve(__dirname, 'index.html'), @@ -35,7 +35,7 @@ export default defineConfig(({ command }) => { }, optimizeDeps: { esbuildOptions: { - target: 'es2020' + target: 'es2022' } } }; diff --git a/packages/examples/workerCom.html b/packages/examples/workerCom.html index 3678b88..7e96576 100644 --- a/packages/examples/workerCom.html +++ b/packages/examples/workerCom.html @@ -8,8 +8,12 @@ - -
Inter-Worker Communication
+
+
Inter-Worker Communication
+ + + +