简体   繁体   中英

Web Worker with imported modules in React

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

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___default is not defined

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

EDIT:

As per suggestions from Kaiido, I have tried installing worker-loader , and edited my webpack.config.js to the following:

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

But when I run it, I still get the error

Uncaught ReferenceError: jimp__WEBPACK_IMPORTED_MODULE_0__ is not defined

I experienced the same problem. Firefox could not show me where exactly the error was (in fact it was plain misleading...) but Chrome did. I fixed my problem by not relying on an import statement (importing one of my other files) which would only have worked within a React context. When you load a Worker script (via the blob() / URL() hack), it has no React context (as it is loaded at runtime and not at transpile time). So all the React paraphernalia __WEBPACK__blah_blah is not going to exist / be visible. So... within react... import statements in worker files will not work. I haven't thought of a workaround yet.

I'm not too much into React, so I can't tell if the module-Worker is the best way to go (maybe worker-loader would be a better solution), but regarding the last error you got, it's because you didn't set the type of your Blob when you built it.

In this case, it does matter, because it will determine the Content-Type the browser sets when serving it to the APIs that fetch it.
Here Firefox is a bit more lenient and somehow allows it, but Chrome is picky and requires you set this type option to one of the many javascript MIME-types.

 const script_content = `postMessage('running');`; // this one will fail in Chrome const blob1 = new Blob([script_content]); // no type option const worker1 = new Worker(URL.createObjectURL(blob1), { type: 'module'}); worker1.onerror = (evt) => console.log( 'worker-1 failed' ); worker1.onmessage = (evt) => console.log( 'worker-1', evt.data ); // this one works in Chrome const blob2 = new Blob([script_content], { type: "text/javascript" }); const worker2 = new Worker(URL.createObjectURL(blob2), { type: 'module'}); worker2.onerror = (evt) => console.log( 'worker-2 failed' ); worker2.onmessage = (evt) => console.log( 'worker-2', evt.data );

But now that this error is fixed, you'll face an other error, because the format import lib from "libraryname" is still not supported in browsers, so you'd have to change "libraryname" to the path to your actual script file, keeping in mind that it will be relative to your Worker's base URI, ie probably your main-page's origin.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM