7

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.

Kiechlus
  • 1,167
  • 12
  • 21

2 Answers2

6

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.
  }
}));
Esailija
  • 138,174
  • 23
  • 272
  • 326
  • Another question: I tried to imitate the ipc-channel by sending JSON over the pipe from the stdout of the child to the parent: `console.log(JSON.stringify(result));` in the child and `JSON.parse(data);` in the parent. But it will always throw an `SyntaxError: Unexpected token u at Object.parse (native)`. What is this ipc-channel doing differently? What is it anyway? – Kiechlus Jan 08 '15 at 09:27
  • @Kiechlus `data` is not a JSON string, maybe try `console.log(data)` to see what it is, most likely `"undefined"` – Esailija Jan 08 '15 at 12:36
1

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));
  }
});
Naeem Shaikh
  • 15,331
  • 6
  • 50
  • 88
  • Hm, but in this case I can't manage to get the stack. Also it prints out "user defined error messegemes{"message":"mes"}" and not the Error Message I gave – Kiechlus Jan 06 '15 at 12:39
  • oh I am sorry, it should be `var Error=function(mes){ this.message=mes; };` – Naeem Shaikh Jan 06 '15 at 12:41
  • ah ja sure, like this it works with the message. Do you have any idea how I can get the stack-trace? – Kiechlus Jan 06 '15 at 12:46