Rendering Without Blocking In A Worker

Rendering items on #canvas in main loop might cause interface freezes, preventing render process from executing properly by flooding execution stack with operations.

To handle it properly, we can start separate #worker thread, that will handle rendering in its own event loop, so that won't affect the source tab's event loop.

Workers can have access to Transferrable Objects from main thread by receiving memory address. That's a lot faster than cloning. In this case ImageBitmap is transferrable.

Code for the main thread component:

// main.tsconst instance = new Worker('./render-worker.ts');const canvas = document.getElementById('view');// attaching event listener to workerinstance.onmessage =: MessageEvent) => {    const ctx = canvas?.getContext("2d");    if (!ctx) {        throw new Error(`Can't get 2D context`);    }        ctx.drawImage(event.data as ImageBitmap, 0, 0);}// sending canvas contents to workerconst renderInCanvas = () => {    if (!canvas.current) {      return;    }        createImageBitmap(canvas.current).then        instance.postMessage(image, { transfer: [image] })    });};

Worker code:

// render-worker.tsexport default () => {  self.onmessage =     const data = message.data;    if (!(data instanceof ImageBitmap)) {      throw new Error('Received unknown data')    }    // OffscreenCanvas can be set up inside workers    const canvas = new OffscreenCanvas(data.width, data.height);    const ctx = canvas.getContext("2d");    if (!ctx) {      throw new Error(`Can't get 2D context`);    }    ctx.drawImage(data, 0, 0);    // That will block execution inside worker for     // a couple of seconds    doHardRenderingStuffHere(ctx, data.width, data.height);    // Sending resulting image back to main thread    createImageBitmap(canvas).then=> {      self.postMessage(image, { transfer: [image] });    });  };};