Skip to content

Commit

Permalink
child_process: Pass child in resolve/reject too
Browse files Browse the repository at this point in the history
  • Loading branch information
awwright committed Jul 14, 2020
1 parent 66810a0 commit 1f36262
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 15 deletions.
38 changes: 27 additions & 11 deletions doc/api/child_process.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,7 @@ encoding, `Buffer` objects will be passed to the callback instead.
const { exec } = require('child_process');
exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
return;
console.error(`exit(${error.code}): ${error}`);
}
console.log(`stdout: ${stdout}`);
console.error(`stderr: ${stderr}`);
Expand All @@ -224,6 +223,8 @@ child runs longer than `timeout` milliseconds.
Unlike the exec(3) POSIX system call, `child_process.exec()` does not replace
the existing process and uses a shell to execute the command.

#### exec with promisify

If this method is invoked as its [`util.promisify()`][]ed version, it returns
a `Promise` for an `Object` with `stdout` and `stderr` properties. The returned
`ChildProcess` instance is attached to the `Promise` as a `child` property. In
Expand Down Expand Up @@ -306,19 +307,34 @@ can be used to specify the character encoding used to decode the stdout and
stderr output. If `encoding` is `'buffer'`, or an unrecognized character
encoding, `Buffer` objects will be passed to the callback instead.

#### execFile with promisify

If this method is invoked as its [`util.promisify()`][]ed version, it returns
a `Promise` for an `Object` with `stdout` and `stderr` properties. The returned
`ChildProcess` instance is attached to the `Promise` as a `child` property. In
case of an error (including any error resulting in an exit code other than 0), a
rejected promise is returned, with the same `error` object given in the
callback, but with two additional properties `stdout` and `stderr`.
a `Promise`, with an additional property `child` with the `ChildProcess` that
would normally be returned by `execFile`.

On a successful exit (a zero status code), the promise resolves to an object
with `child`, `stdout`, and `stderr` properties.

On an unsuccessful exit, the promise resolves to the same `error` returned in
the callback, with three additional properties `child`, `stdout`, `stderr`,
which provide the output of the process. As with `exec`, the exit code is
attached to the `code` property.

```js
const util = require('util');
const execFile = util.promisify(require('child_process').execFile);
const { promisify } = require('util');
const execFile = promisify(require('child_process').execFile);
async function getVersion() {
const { stdout } = await execFile('node', ['--version']);
console.log(stdout);
try {
const { stdout, stderr, child } = await execFile('node', ['--version']);
console.log(`pid ${child.pid} exit`);
console.log(`stdout: ${stdout}`);
console.error(`stderr: ${stderr}`);
} catch (error) {
console.error(`pid ${error.child.pid} exit(${error.code}): ${error}`);
console.log(`stdout: ${error.stdout}`);
console.error(`stderr: ${error.stderr}`);
}
}
getVersion();
```
Expand Down
5 changes: 3 additions & 2 deletions lib/child_process.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,14 @@ const customPromiseExecFunction = (orig) => {
reject = rej;
});

promise.child = orig(...args, (err, stdout, stderr) => {
const child = promise.child = orig(...args, (err, stdout, stderr) => {
if (err !== null) {
err.child = child;
err.stdout = stdout;
err.stderr = stderr;
reject(err);
} else {
resolve({ stdout, stderr });
resolve({ child, stdout, stderr });
}
});

Expand Down
12 changes: 10 additions & 2 deletions test/parallel/test-child-process-promisified.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ const execFile = promisify(child_process.execFile);

assert(promise.child instanceof child_process.ChildProcess);
promise.then(common.mustCall((obj) => {
assert.deepStrictEqual(obj, { stdout: '42\n', stderr: '' });
assert(obj.child instanceof child_process.ChildProcess);
assert.strictEqual(obj.stdout, '42\n');
assert.strictEqual(obj.stderr, '');
}));
}

Expand All @@ -21,7 +23,9 @@ const execFile = promisify(child_process.execFile);

assert(promise.child instanceof child_process.ChildProcess);
promise.then(common.mustCall((obj) => {
assert.deepStrictEqual(obj, { stdout: '42\n', stderr: '' });
assert(obj.child instanceof child_process.ChildProcess);
assert.strictEqual(obj.stdout, '42\n');
assert.strictEqual(obj.stderr, '');
}));
}

Expand All @@ -31,6 +35,7 @@ const execFile = promisify(child_process.execFile);
assert(promise.child instanceof child_process.ChildProcess);
promise.catch(common.mustCall((err) => {
assert(err.message.includes('doesntexist'));
assert(err.child instanceof child_process.ChildProcess);
}));
}

Expand All @@ -40,13 +45,15 @@ const execFile = promisify(child_process.execFile);
assert(promise.child instanceof child_process.ChildProcess);
promise.catch(common.mustCall((err) => {
assert(err.message.includes('doesntexist'));
assert(err.child instanceof child_process.ChildProcess);
}));
}
const failingCodeWithStdoutErr =
'console.log(42);console.error(43);process.exit(1)';
{
exec(`${process.execPath} -e "${failingCodeWithStdoutErr}"`)
.catch(common.mustCall((err) => {
assert(err.child instanceof child_process.ChildProcess);
assert.strictEqual(err.code, 1);
assert.strictEqual(err.stdout, '42\n');
assert.strictEqual(err.stderr, '43\n');
Expand All @@ -56,6 +63,7 @@ const failingCodeWithStdoutErr =
{
execFile(process.execPath, ['-e', failingCodeWithStdoutErr])
.catch(common.mustCall((err) => {
assert(err.child instanceof child_process.ChildProcess);
assert.strictEqual(err.code, 1);
assert.strictEqual(err.stdout, '42\n');
assert.strictEqual(err.stderr, '43\n');
Expand Down

0 comments on commit 1f36262

Please sign in to comment.