10

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.

1 Answer 1

11

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');
    
3
  • Thanks for you time. In fact, I am trying the first solution and it does not work. I did it correctly but it does not work and throw TypeError: obj.c is not a function again. I do not know why but the ``Òbject.SetPrototypeOf()``` seems to be not effective here. For your second solution, I should create a new class constructor to initialize a new object? I hope I will be able to use the first method because I manipulated one heavy class instance so it will be less heavy to only set prototype if I can.
    – Helloïs
    Nov 20, 2020 at 14:56
  • 1
    @Helloïs - My mistake. I had a typo. It should be Object.setPrototypeOf(obj, NewObj.prototype);. Try that. It needs to be the prototype of your class, not the class itself.
    – jfriend00
    Nov 20, 2020 at 15:09
  • Let's go !!! It works very well. Thanks for your time @jfriend00
    – Helloïs
    Nov 20, 2020 at 15:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.