Promise resolving to child stream stdout and rejec

2019-07-08 04:53发布

问题:

I'd like to build a promise that spawns a child process using require('child_process').spawn. The process streams its output to stdout and its errors to stderr.

I would like the promise to:

  • reject(child.stderr stream (or its data)) if child.stderr emits any data.
  • resolve(child.stdout stream) only if no error is emitted.

I'm doing this because I want to chain the promise to:

  • a then that processes the child.stdout stream (upload the stream to an S3 bucket).
  • a catch that can process the child.stderr stream, allowing me to properly handle errors.

Is it feasible to combine promises and process streams like this ? I was thinking of working around stderr but unsure about whats happening in between to stdout if a lot of data is coming into it and I don't process it fast enough.

回答1:

As I see it, the issue is that you don't know whether you ever got data on stderr until the entire process is done as it could put data there at any time.

So, you have to wait for the entire process to be done before calling resolve() or reject(). And, if you then want the entire data to be sent to either one of those, you'd have to buffer them. You could call reject() as soon as you got data on stderr, but you aren't guaranteed to have all the data yet because it's a stream.

So, if you don't want to buffer, you're better off just letting the caller see the streams directly.

If you are OK with buffering the data, you can buffer it yourself like this:

Based on the spawn example in the node.js doc, you could add promise support to it like this:

const spawn = require('child_process').spawn;

function runIt(cmd, args) {
    return new Promise(function(resolve, reject) {
        const ls = spawn(cmd, args);

        // Edit thomas.g: My child process generates binary data so I use buffers instead, see my comments inside the code 
        // Edit thomas.g: let stdoutData = new Buffer(0)
        let stdoutData = "";
        let stderrData= "";
        ls.stdout.on('data', (data) => {
        // Edit thomas.g: stdoutData = Buffer.concat([stdoutData, chunk]);
            stdoutData += data;
        });

        ls.stderr.on('data', (data) => {
            stderrData += data;
        });

        ls.on('close', (code) => {
            if (stderrData){
                reject(stderrData);
            } else {
                resolve(stdoutData);
            }
        });
        ls.on('error', (err) => {
            reject(err);
        });
    }) 
}

//usage
runIt('ls', ['-lh', '/usr']).then(function(stdoutData) {
    // process stdout data here
}, function(err) {
    // process stdError data here or error object (if some other type of error)
});