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

1

9

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.

Dryden answered 19/11, 2020 at 21:42 Comment(0)
T
10

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');
    
Tavares answered 20/11, 2020 at 0:56 Comment(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.Worldlywise
@Dryden - 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.Tavares
Let's go !!! It works very well. Thanks for your time @TavaresWorldlywise

© 2022 - 2024 — McMap. All rights reserved.