Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sending an Error Object from a spawned child-process over an IPC-channel

I enabled communication between parent and child process in order to send JSON as follows:

Child:

try {  
  var price1 = parseInt(process.argv[2]);
  if (!price1) {
     throw new Error('Price in calculations.js undefined');
  }
  var result = {
    'timeStamp' : Date(),
    'prices' : { 'player1' : price1, 'player2' : 666}
  };
  process.send(result);
} catch (e) {
  // In case of an error, I get here as expected.
  process.send(e);
}

Parent:

var spawn = require('child_process').spawn;
var child = spawn('node', ['calculations.js', 333],  {stdio: [null,null,'pipe','ipc']});
child.on('message', function(data) {    
  if (data instanceof Error) {
    // In case of an error, this is never reached.
  } else {
    // do sthing with JSON object.
  }
});

The JSON thing works fine. But if I provoke an error, it doesn't work. I want to send the entire error-object (with message and stack-trace) from child to parent. But it doesn't seem to be an instance of error what I am sending.

like image 374
Kiechlus Avatar asked Jan 06 '15 10:01

Kiechlus


People also ask

What is spawn child process?

The child_process. spawn() method launches a new process with a given command. This method returns streams (stdout & stderr) and it is generally used when the process returns large amount of data. Syntax: child_process.

How do you spawn a child process in node?

For example, here's code to spawn a new process that will execute the pwd command. const { spawn } = require('child_process'); const child = spawn('pwd');

What is node Child_process?

The node:child_process module provides the ability to spawn subprocesses in a manner that is similar, but not identical, to popen(3) . This capability is primarily provided by the child_process.

What is the use of child process?

A child process is a process created by a parent process in operating system using a fork() system call. A child process may also be called a subprocess or a subtask. A child process is created as its parent process's copy and inherits most of its attributes.


2 Answers

Processes don't share memory so the only way to communicate is with strings, objects are JSON serialized on send and JSON parsed back on receive. Error objects don't serialize well by default:

JSON.stringify(new Error())
"{}"

Also, JSON parsed object is untyped so instanceof cannot work.

You can make serialization hook for errors objects:

Error.prototype.toJSON = function() {
    var ret = {
        name: this.name,
        message: this.message,
        stack: this.stack,
        __error__: true
    };
    // Add any custom properties such as .code in file-system errors
    Object.keys(this).forEach(function(key) {
        if (!ret[key]) {
            ret[key] = this[key];
        }
    }, this);
    return ret;
};

After that method definition error objects serialize better:

 JSON.stringify(new Error())

"{"name":"Error","message":"","stack":"Error\n    at <anonymous>:2:16\n    at Object.InjectedScript._evaluateOn (<anonymous>:762:137)\n    at Object.InjectedScript._evaluateAndWrap (<anonymous>:695:34)\n    at Object.InjectedScript.evaluate (<anonymous>:609:21)","__error__":true}"

Then reconstruct it automatically:

function getMessageReceiver(fn) {
    return function(data) {
        var result = data;
        if (data && data.__error__) {
            result = new Error();
            result.message = data.message;
            result.stack = data.stack;
            result.name = data.name;
            Object.keys(data).forEach(function(key) {
                if (!result[key]) {
                    result[key] = data[key];
                }
            });
        }
        return fn.call(this, result);
    }
}

And finally:

child.on('message', getMessageReceiver(function(data) {    
  if (data instanceof Error) {
    console.log(data.stack); // Stack is from child process
  } else {
    // do sthing with JSON object.
  }
}));
like image 125
Esailija Avatar answered Oct 06 '22 13:10

Esailija


This is something I tried and works,

var Error=function(mes){
    this.message=mes;
};

try {  
  var price1 = parseInt(process.argv[4]);
 if (!price1) {
     throw new Error('Price in calculations.js undefined');
 }
  var result = {
    'timeStamp' : Date(),
    'prices' : { 'player1' : price1, 'player2' : 666}
  };  console.log("inside try");
  process.send(result);
} catch (e) {
  // In case of an error, I get here as expected.
  console.log("inside catch");
  process.send(e);
}

First create object Error before throwing it, otherwise it just pass an empty object which is not instanceof Error.

And the parent

var child = require('child_process').fork(__dirname + '/SO2.js', [333],  {stdio: [null,null,'pipe','ipc']});

child.on('message', function(data) {
  if(data.timeStamp){
    console.log("result received ");
  } 
  else{
    // do sthing with JSON object.
    console.log("user defined error messege"+data.message + JSON.stringify(data));
  }
});
like image 33
Naeem Shaikh Avatar answered Oct 06 '22 13:10

Naeem Shaikh