10

So I am working on a project in Node.js and I want to open up some extra threads to handle the processing load more efficiently. But I am using classes with function definitions with them and when I try to send those objects to the worker thread, the functions defined in the object disappear and I am only left with the other fields in the object. Is there a way to send the worker an object and preserve the functions so they can be called within the worker?

var cluster = require('cluster');

if(cluster.isMaster){
    Monster = function(species){
        this.attack = function(){
            console.log('CHOMP');
        };
        this.name = species;
    };
    var vamp = new Monster('vampire'),
    worker   = cluster.fork();
    worker.send({'monster' : vamp});
    }
else{
    process.on('message', function(msg) {
        console.log(msg.monster); //this logs "{ name: 'vampire' }"  
        msg.monster.attack(); //TypeError: Object #<Object> has no method 'attack'
    });
}
Charlie Lipford
  • 121
  • 1
  • 6
  • 1
    Perplexed: the title and text of your question are interesting and make sense for me, to date in 2021. But the code example is unrelated/obsolete, because it shows how to use nodejs cluster of PROCESSES instead of of nodejs WORKER THREADS. Even if I realize that in 2012 nodejs didn't contain worker threads. – Giorgio Robino Apr 26 '21 at 20:11

3 Answers3

6

No, there is no way to pass functions between threads. You can pass only JS plain objects (data only) and handle it with functions defined in current thread (like create new object with received data).

Vadim Baryshev
  • 25,689
  • 4
  • 56
  • 48
  • 2
    wow that really limits the functionality of the cluster module. Do you know if there are any plans to add that to Node.js? – Charlie Lipford Sep 06 '12 at 18:06
  • 1
    This functionality is contrary to node.js architecture. Node.js event-loop works in single thread. Cluster module just starts new processes. They all have their own independent memory. Implementation of multi-threaded architectures need to support threads with shared memory and related mechanisms such as `mutexes` and `semaphores`. This is not node.js way. – Vadim Baryshev Sep 06 '12 at 18:45
  • Upvoted. See details about why functions can not be shared between threads: 1. https://nodejs.org/api/worker_threads.html#worker_threads_worker_workerdata and 2. https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm – Giorgio Robino Apr 26 '21 at 09:58
3

Charlie, I realize you asked this question a year ago, but I was wanting to do something very similar and I came across your question which you didn't mark an answer to yet. I thought I would take a "stab" at it and show you what I have done with your code. This different way of organizing code is for me a very acceptable workaround in my node.js work. I am pretty sure this gives you a way to accomplish what you want, even though you can't do it in the manner you wanted.

Declare your "class" outside the cluster code, like this:

var cluster = require('cluster');

var Monster = function(species){
    this.attack = function(){
        console.log('CHOMP!');
    };
    this.die = function() {
        console.log("Oh, what did I eat?  I don't feel so good....\r\n");
        process.exit(0);
    };
    this.scare = function() {
        console.log("BOO! I am a " + this.name + "!");
    };
    this.name = species;
};

if(cluster.isMaster){
    worker = cluster.fork();
    worker.send({'species' : 'Vampire'});
}
else{
    process.on('message', function(msg) {
        if(typeof msg.species !== "undefined") {
            myMonster = new Monster(msg.species);
            myMonster.scare();
            myMonster.attack();
            myMonster.die();
        }
    });
}

Give that a whirl and see if this is an answer you can accept!

Geek Stocks
  • 2,010
  • 3
  • 27
  • 43
  • Smart solution/maybe the only feasible. But in your code there is no sharing of an "object with functions to worker thread", as the title of the question states. There are no worker threads, you just fork a cluster of processes where each process create his own object(s). Nice but the "worst case" option, not suitable if you want to share big data object(s) and/or containg functions (references). – Giorgio Robino Apr 26 '21 at 20:03
0

Ok, stumbled upon this answer, and I found it strange that no one brought this up, but it might be a more modern feature than the question:

eval

let str = "() => { console.log('test') }"
let func = eval(str)
func()

Think it's obvious what's going on here, you can parse any string to javascript, and you can send strings to workers, so you can build and object with functions:

let obj = { a: "() => { ... }" }

and send the object over. (JSON.stringify(obj) first, and than you will have to parse the object first, and than all the substrings seperately)

Craig O'Connor
  • 153
  • 3
  • 10
  • 1
    This is potentially dangerous, especially on a Node server which has access to other parts of the system. Imagine someone uploads a file named `return (function () { /* arbitrary code*/ })();`, and in your class, and somehow that file name ends up getting passed to `eval` instead of your function. It's unlikely, but using `eval` in server code just opens up more vulnerabilities to your system. – Austin Davis Mar 05 '20 at 18:27
  • I completely agree if you are going to evaluate a request. However, in the case of the OP, he's sending his own code over to the worker, so 'eval' is honestly the best solution IMO. – Craig O'Connor Mar 06 '20 at 16:06
  • 2
    `'eval' is honestly the best solution`, unless you knew you could just [serialise functions](https://stackoverflow.com/a/51122739/2518317). Honestly, *if* the function in question isn't the one doing the compute, wrapping it up as a callback listening for its own personalised `postMessage` is actually the best solution (with the added bonus that any closures and class instances continue to work; because they never left the originating thread) – Hashbrown May 20 '21 at 04:53