Search code examples
javascriptnode.jsparallel-processingweb-workerchild-process

Inline Child Processes in Node.js


I'm familiar with Web Workers in the browser and figured that the Node.js equivalent of doing

const worker = new Worker('./worker')

is

const worker = child_process.fork('./worker')

The API is a bit different, and they don't quite work the same under the hood but in the end Web Workers and Child Processes seem to both do roughly the same thing; allow you to run JavaScript code in parallel.

Now, there is this neat thing that can be done with Web Workers where instead of creating a Worker by passing the path/url of the file containing the worker code you can pass an actual function. This can be achieved with this simple three line function:

function createWorker(fn) {
  var blob = new Blob(['self.onmessage = ', fn.toString()], { type: 'text/javascript' });
  var url = URL.createObjectURL(blob);

  return new Worker(url);
}

This allows you to write your worker code inline, in the main/master/parent (whatever you wanna call it) file, like this:

var myWorker = createWorker(function (e) {
  self.postMessage(e.data.toUpperCase());
});

myWorker.onMessage = function (e) {
  console.log(e.data); // HELLO FROM AN INLINE WORKER!
}

myWorker.postMessage('hello from an inline worker!')

QUESTION
How can I achieve the same thing with Node.js Child Processes? I couldn't tell from looking at the documentation whether or not you could pass something other than a module path to child_process.fork or if there was any other way to achieve what I want; to write inline child processes.

UPDATE
I have tried the following, based on @Bergi 's suggestion in the comments:

const fileSync = require('tmp').fileSync;
const writeFileSync = require('fs').writeFileSync;
const fork = require('child_process').fork;

function createWorker(fn) {
  const tmpobj = fileSync();
  writeFileSync(tmpobj.name, fn.toString()); 

  return fork(tmpobj.name);
}

var myWorker = createWorker(function (e) {
  process.send(e.toUpperCase());
});

myWorker.on('message', function (e) {
  console.log(e); // HELLO FROM AN INLINE WORKER!
})

myWorker.send('hello from an inline worker!');

Alas, I am getting the following error:

C:\Users\phili\AppData\Local\Temp\tmp-165566BgqUKKM5yjR.tmp:1
(function (exports, require, module, __filename, __dirname) { function (e) {
                                                                       ^

SyntaxError: Unexpected token (
    at createScript (vm.js:80:10)
    at Object.runInThisContext (vm.js:139:10)
    at Module._compile (module.js:616:28)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Function.Module.runMain (module.js:693:10)
    at startup (bootstrap_node.js:188:16)
    at bootstrap_node.js:609:3

I'm afraid I am out of my depth here. Any idea what is going on and how I can get it to work?


Solution

  • Here's what ended up working for me:

    const fileSync = require('tmp').fileSync;
    const writeFileSync = require('fs').writeFileSync;
    const fork = require('child_process').fork;
    
    
    
    function createWorker(fn) {
      const tmpobj = fileSync();
      writeFileSync(tmpobj.name, `process.on('message', ${fn.toString()})`); 
    
      return fork(tmpobj.name);
    }
    
    var myWorker = createWorker(function (e) {
      process.send(e.toUpperCase());
    });
    
    myWorker.on('message', function (e) {
      console.log(e); // HELLO FROM AN INLINE WORKER!
    })
    
    myWorker.send('hello from an inline worker!');
    

    A million thanks to @Bergi for his help in figuring this out.