0

I'm not quite sure why this is happening, however I have the following which I wrote:

const runScript = (cmd, args) => {
  return new Promise((res, rej) => {
    // spawn a new process
    const ls = spawn(cmd, args);

    ls.stdout.on("data", (data) => {process.stdout.write(data)});

    ls.stderr.on("data", (data) => {process.stderr.write(data)});

    ls.on("close", code => {
      console.log(`Command ${cmd} ${args} exited with code ${code}`);
      code === 0 ? res(code) : rej(code);
    });

    ls.on("error", code => {
      rej(code);
    });
  });
};

This works fine. But I figured I could change the .on("data") event handlers to just pass in the write function directly. So I switched from

ls.stdout.on("data", (data) => {process.stdout.write(data)});

To

ls.stdout.on("data", process.stdout.write);

Depending on the command, I either get no output with the refactored version (passing .write directly) or an EPIPE. I thought they were the exact same. What's happening exactly here? I'm suspecting it has something to do with either the buffering or what process is referring to.

rb612
  • 5,280
  • 3
  • 30
  • 68
  • 1
    I hope, this question will help you. It's based on the same concept. https://stackoverflow.com/q/9052329/3439731 – Raghav Garg Oct 19 '19 at 08:09

1 Answers1

2

When you pass process.stdout.write as an argument, it reads the value of process.stdout.write and passes JUST the function pointer to the write method. Then, when ls.staout.on() calls that method sometime later, it's not bound to process.stdout and it does not work properly.

Instead, change to this:

ls.stdout.on("data", process.stdout.write.bind(process.stdout));

And, it will be properly bound to the desired object when it is called.

See How this is set inside a function call for a summary of how this is controlled inside a function.


As a bit simpler example, see this:

class X {
    constructor(val) {
        this.val = val;
    }
    add(otherVal) {
       return this.val + otherVal;
    }
}

let x = new X(3);
let fn = x.add;
fn(5);               // doesn't work properly

Examples of things that would work properly:

let x = new X(3);
x.add(5);       // obj.method() sets proper value of this to obj in the method

Or, if you want to pass around the method:

let x = new X(3);
let fn = x.add;
fn.call(x, 5);   // fn.call(x) artificially sets this to x inside the method

Or this:

let x = new X(3);
let fn = x.add.bind(x);  // creates stub function that calls method properly
fn(5);

This won't work either because when fn(5) is called, there's no binding to the x object so when the function runs, the reference to this inside the add() method won't have a proper value of this and it won't work properly.

When this code does let fn = x.add, it gets a pointer to the add method, but there's no binding to the x object. That gets lost. Then, when you call fn(5), since the this value in Javascript is set according to how a function is called, this is just an ordinary function call so the this value is set to undefined (in strict mode) or the global object (when not in strict mode). In either case, it's not the desired x object. To make it the x object, it has to be called as x.fn() of the this value has to be artificially set some other method such as with .apply(), .call() or .bind().

In your case above, since you don't control the caller of the function (it's being called by other code that you don't control), then .bind() is an appropriate solution.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • @rb612 - Did this answer your question? If so, you can indicate that to the community here by clicking the checkmark to the left of the answer. That will also earn you some reputation points here on stackoverflow for following the proper procedure. If this didn't answer your question, please comment about which part of your question you're still confused about. – jfriend00 Nov 25 '19 at 18:13