child_process: attach child in promisification

This commit updates the custom exec() and execFile()
promisification to attach the ChildProcess instance to the
returned Promise.

PR-URL: https://github.com/nodejs/node/pull/28325
Fixes: https://github.com/nodejs/node/issues/28244
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Wyatt Preul <wpreul@gmail.com>
This commit is contained in:
cjihrig 2019-06-20 12:10:06 -04:00
parent 05359dd0db
commit 3aeb810ca6
No known key found for this signature in database
GPG Key ID: 7434390BDBE9B9C5
3 changed files with 42 additions and 21 deletions

View File

@ -214,10 +214,11 @@ Unlike the exec(3) POSIX system call, `child_process.exec()` does not replace
the existing process and uses a shell to execute the command. the existing process and uses a shell to execute the command.
If this method is invoked as its [`util.promisify()`][]ed version, it returns If this method is invoked as its [`util.promisify()`][]ed version, it returns
a `Promise` for an `Object` with `stdout` and `stderr` properties. In case of an a `Promise` for an `Object` with `stdout` and `stderr` properties. The returned
error (including any error resulting in an exit code other than 0), a rejected `ChildProcess` instance is attached to the `Promise` as a `child` property. In
promise is returned, with the same `error` object given in the callback, but case of an error (including any error resulting in an exit code other than 0), a
with an additional two properties `stdout` and `stderr`. rejected promise is returned, with the same `error` object given in the
callback, but with an additional two properties `stdout` and `stderr`.
```js ```js
const util = require('util'); const util = require('util');
@ -295,9 +296,10 @@ stderr output. If `encoding` is `'buffer'`, or an unrecognized character
encoding, `Buffer` objects will be passed to the callback instead. encoding, `Buffer` objects will be passed to the callback instead.
If this method is invoked as its [`util.promisify()`][]ed version, it returns If this method is invoked as its [`util.promisify()`][]ed version, it returns
a `Promise` for an `Object` with `stdout` and `stderr` properties. In case of an a `Promise` for an `Object` with `stdout` and `stderr` properties. The returned
error (including any error resulting in an exit code other than 0), a rejected `ChildProcess` instance is attached to the `Promise` as a `child` property. In
promise is returned, with the same `error` object given in the 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 an additional two properties `stdout` and `stderr`. callback, but with an additional two properties `stdout` and `stderr`.
```js ```js

View File

@ -149,17 +149,24 @@ function exec(command, options, callback) {
const customPromiseExecFunction = (orig) => { const customPromiseExecFunction = (orig) => {
return (...args) => { return (...args) => {
return new Promise((resolve, reject) => { let resolve;
orig(...args, (err, stdout, stderr) => { let reject;
if (err !== null) { const promise = new Promise((res, rej) => {
err.stdout = stdout; resolve = res;
err.stderr = stderr; reject = rej;
reject(err);
} else {
resolve({ stdout, stderr });
}
});
}); });
promise.child = orig(...args, (err, stdout, stderr) => {
if (err !== null) {
err.stdout = stdout;
err.stderr = stderr;
reject(err);
} else {
resolve({ stdout, stderr });
}
});
return promise;
}; };
}; };

View File

@ -8,25 +8,37 @@ const exec = promisify(child_process.exec);
const execFile = promisify(child_process.execFile); const execFile = promisify(child_process.execFile);
{ {
exec(`${process.execPath} -p 42`).then(common.mustCall((obj) => { const promise = exec(`${process.execPath} -p 42`);
assert(promise.child instanceof child_process.ChildProcess);
promise.then(common.mustCall((obj) => {
assert.deepStrictEqual(obj, { stdout: '42\n', stderr: '' }); assert.deepStrictEqual(obj, { stdout: '42\n', stderr: '' });
})); }));
} }
{ {
execFile(process.execPath, ['-p', '42']).then(common.mustCall((obj) => { const promise = execFile(process.execPath, ['-p', '42']);
assert(promise.child instanceof child_process.ChildProcess);
promise.then(common.mustCall((obj) => {
assert.deepStrictEqual(obj, { stdout: '42\n', stderr: '' }); assert.deepStrictEqual(obj, { stdout: '42\n', stderr: '' });
})); }));
} }
{ {
exec('doesntexist').catch(common.mustCall((err) => { const promise = exec('doesntexist');
assert(promise.child instanceof child_process.ChildProcess);
promise.catch(common.mustCall((err) => {
assert(err.message.includes('doesntexist')); assert(err.message.includes('doesntexist'));
})); }));
} }
{ {
execFile('doesntexist', ['-p', '42']).catch(common.mustCall((err) => { const promise = execFile('doesntexist', ['-p', '42']);
assert(promise.child instanceof child_process.ChildProcess);
promise.catch(common.mustCall((err) => {
assert(err.message.includes('doesntexist')); assert(err.message.includes('doesntexist'));
})); }));
} }