javascriptnode.jsmultithreadingworker-threadnode-worker-threads

How to pass class instances with function through worker_thread.Worker to use function?


To contextualize, I would like to use class instances functions through a Worker Thread from NodeJS "worker_thread" module.

In one main.js file I declare a class and instanciate a new Worker passing the new instance through the workerData option.

main.js

const { Worker } = require('worker_threads');

class obj {
    constructor() {
        this.a = "12";
        this.b = 42;
    }

    c() {
        return 'hello world';
    }
}

let newobj = new obj();
console.log({
    a: newobj.a,
    b: newobj.b,
    c: newobj.c()
});
//Output: { a: '12', b: 42, c: 'hello world' }

let worker = new Worker('./process.js', { workerData: { obj: newobj } });
worker.once('message', res => { console.log({ res }) });

As you may see, the worker called the process.js script. Let see it.

process.js

const { parentPort, workerData } = require('worker_threads');

let { obj } = workerData;

console.log({
    a: obj.a,
    b: obj.b,
    c: obj.c()
});

parentPort.postMessage('DONE');

As you know, this code throw an error: TypeError: obj.c is not a function. In fact, after checking Worker Thread documentation (https://nodejs.org/dist./v10.22.0/docs/api/worker_threads.html#worker_threads_new_worker_filename_options) I discover that Worker could not be an Object with function. In reality, it works but all functions are not cloned. Screen capture of worker_thread workerData options definition in NodeJS official documentation

I am really confused because I do not know how to solve this problem. In fact, this exemple is easy because of simplify a complex situation but in my case I absolutly need to call the c function in process.js side but I do not know how to do it differently.

I hope you may help me. Thanks in advance for your time.


Solution

  • The issue is that data is sent between parent and worker as a plain object (without the prototype). So, to make it into a class again, in the worker you need both the class code and to reassign the prototype.

    First, you can put the class code into its own module so you can import it into both the parent and the worker. Then, in the worker, you can assign the prototype to the object you get.

    const { parentPort, workerData } = require('worker_threads');
    let { obj } = workerData;
    const NewObj = require('./newobj');
    Object.setPrototypeOf(obj, NewObj.prototype);
    
    console.log({
        a: obj.a,
        b: obj.b,
        c: obj.c()
    });
    
    parentPort.postMessage('DONE');
        
    

    Another way you could do it is to import the class code and then make a constructor option for that class that takes the data from a plain object an initializes a new class instance with it.

    const { parentPort, workerData } = require('worker_threads');
    const NewObj = require('./newobj');
    
    // create a NewObj from scratch and let it initialize itself from the object that
    // was passed to our worker
    let obj = new NewObj(workerData.obj);
    
    console.log({
        a: obj.a,
        b: obj.b,
        c: obj.c()
    });
    
    parentPort.postMessage('DONE');