Search code examples
javascriptthree.jsrequirejsweb-worker

What's the right way to use require.js with web-workers?


Currently, I'm working on porting an existing web app to require.js. Most things seem to work, but the functionality that uses web-workers. For example, I had a worker, defined in a separate js file MeshLoader.js, that loads a 3D model from an STL file:

importScripts('../lib/three.min.js', '../lib/STLLoader.js');

onmessage = function(e) {
    var blob = e.data;
    var reader = new FileReaderSync();
    readContents(reader.readAsArrayBuffer(blob));
};

function readContents(contents) {
    try {
        var geometry = new THREE.STLLoader().parse(contents);
    } catch (e) {
        // error handling
    }

    var attributes = {};
    // parsing the file is going on here
    // ...

    postMessage({
            status: 'completed',
            attributes: attributes,
    });
}

A small remark: STLLoader.js module is a three.js plugin that defines STLLoader object and adds it to THREE namespace. This is how I rewrote it with require.js:

importScripts('../lib/require.min.js');

require({
    baseUrl: '../lib'
}, [
    'require', 'three.min', 'stlloader'
],
function(require, THREE, STLLoader) {
    onmessage = function(e) {
        var blob = e.data;
        var reader = new FileReaderSync();
        readContents(reader.readAsArrayBuffer(blob));
    };

    function readContents(contents) {
        try {
            var geometry = new THREE.STLLoader().parse(contents);
        } catch (e) {
            // error handling
        }

        var attributes = {};
        // same code as in the initial version
        // ...

        postMessage({
            status: 'completed',
            attributes: attributes,
        });
    }

    return onmessage;
});

The worker is called the following way:

var worker = new Worker('js/workers/MeshLoader.js');
worker.postMessage(blob);
worker.onmessage = function (event) {
    if (event.data.status == 'completed') {
        // ...
    } else if (event.data.status == 'failed') {
       // ...
    } else if (event.data.status == 'working') {
       // ...
    }
};

So, the problem is it seems the worker isn't called at all. Maybe I need to declare it as a module in requirejs.config() section and then add the module as a dependency to other modules calling this worker?


Solution

  • I use it like this (jsfiddle):

    importScripts("require.js");
    requirejs.config({
        //Lib path
        baseUrl: '.',
        // Some specific paths or alternative CDN's
        paths: {
            "socket.io": [
                "//cdn.socket.io/socket.io-1.3.7",
                "socket.io.backup"]
        },
        waitSeconds: 20
    });
    
    
    requirejs(["LibName"], (TheLibrary) = > {
      // code after all is loaded
      self.onmessage = (msg)=>{
        // do stuff
        TheLibrary.doStuff(msg.data);
      }
      // I tend to dispatch some message here
      self.postMessage("worker_loaded");
    }
    

    Note that it is only after you receive "worker_loaded" that you can post messages to the worker, because only then the message will be accepted. Before, the onmessage callback is not yet estabilished. So your main code should look:

    var worker = new Worker("myworker.js");
    worker.onmessage = function(e) {
      if(e.data=="worker_loaded") { 
        // Tell worker to do some stuff
      }
    }