Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 380
snowfrogdev Avatar asked Feb 17 '26 15:02

snowfrogdev


2 Answers

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.

like image 110
snowfrogdev Avatar answered Feb 20 '26 03:02

snowfrogdev


With worker_threads, there's now an actual possibility to create threads through inline code without the need for temporary files (like shown by snowfrogdev).

const { Worker } = require("worker_threads");
new Worker(`console.log(require("process").env.HOME)`, { eval: true });

In this example, setting eval to true allows the usage of nodejs builtins too.

like image 28
Tim Daubenschütz Avatar answered Feb 20 '26 05:02

Tim Daubenschütz