4

I'm trying to make a web worker to prevent stalling the React main thread. The worker is supposed to read an image and do various things.

The app was created using create-react-app.

Currently I have

webpack.config.js

module.exports = {
    module: {
        rules: [
            {
                test: /\.worker\.js$/,
                use: { loader: 'worker-loader' }
            }
        ]
    }
};

WebWorker.js

export default class WebWorker {
    constructor(worker) {
        const code = worker.toString();
        const blob = new Blob(['('+code+')()'], {type: "text/javascript"});
        return new Worker(URL.createObjectURL(blob),  {type: 'module'});
    }
}

readimage.worker.js

import Jimp from "jimp";

export default () => {
    self.addEventListener('message', e => { // eslint-disable-line no-restricted-globals
        if (!e) return;
        console.log('Worker reading pixels for url', e.data);
        let data = {};

        Jimp.read(e.data).then(image => {
            // jimp does stuff
            console.log('Worker Finished processing image');
        })

        postMessage(data);
    })
};

And then in my React component AppContent.js I have

import WebWorker from "./workers/WebWorker";
import readImageWorker from './workers/readimage.worker.js';

export default function AppContent() {
    const readWorker = new ReadImageWorker(readImageWorker);
    readWorker.addEventListener('message', event => {
        console.log('returned data', event.data);
        setState(data);
    });

    // callback that is executed onClick from a button component
    const readImageContents = (url) => {
        readWorker.postMessage(url);
        console.log('finished reading pixels');
    };
}

But when I run it, I get the error

Uncaught ReferenceError: jimp__WEBPACK_IMPORTED_MODULE_0__ is not defined

How can I properly import a module into a web worker?

cclloyd
  • 8,171
  • 16
  • 57
  • 104
  • import in workers is not widely supported yet - looks like Chrome, Edge, and node right now, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Browser_compatibility. have you tried importScripts? https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Importing_scripts_and_libraries – James South Apr 09 '20 at 05:43
  • @JamesSouth I looked at using importScripts, but besides it gives a linting error (which I can set to ignore), where am I supposed to import the module from? I can't use the relative path in the node_modules folder, nor just `jimp`. The only way to use importScripts with it is to copy the module to the `public` folder and then use an absolute url (such as `http://localhost:3000/jimp.js`). Is there a better way to do that? – cclloyd Apr 09 '20 at 09:52
  • 2
    Maybe worker-plugin https://github.com/GoogleChromeLabs/worker-plugin instead of worker-loader would help?? – James South Apr 09 '20 at 21:15

2 Answers2

1

You might have a problem with webpack importing your worker in the WebWorker class. Try returning new Worker(URL.createObjectURL(blob), import.meta.url)); like how it's documented in the webpack docs.

droid001
  • 163
  • 10
  • thank you very much. in webpack5 fine well for me. – mohammad barzegar Feb 07 '22 at 20:31
  • 1
    You are misquoting the document you linked to and your are using the Worker constructor incorrectly. From your link: const worker = new Worker(new URL('./deep-thought.js', import.meta.url)); The document you linked to sends import.meta.url to the URL constructor. You are sending it to the Worker constructor. You are using URL.createObjectURL your link is using new URL. https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL https://developer.mozilla.org/en-US/docs/Web/API/URL/URL https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker – LoneSpawn Dec 06 '22 at 22:52
0

In my next.js typescript project, I use this code and answer in my use case:

  • function in onClick prop on the element at next.js
async () => {
  const testWorker = new Worker(
    new URL("./test.worker.ts", import.meta.url), {
      name: "test",
      type: "module",
    });
  testWorker.onmessage = function (event) {
    console.log("data:", event.data, "\n", "event:", event);
  };

  testWorker.postMessage([0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
}


  • test.worker.ts
self.onmessage = function ({ data }) {
  self.postMessage((data as number[]).map((d) => d * 10));
};

export {};