Skip to content

Commit dd5e07f

Browse files
cjihrigtargos
authored andcommitted
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: #28325 Fixes: #28244 Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Wyatt Preul <[email protected]>
1 parent 632fc1f commit dd5e07f

File tree

3 files changed

+42
-21
lines changed

3 files changed

+42
-21
lines changed

doc/api/child_process.md

+9-7
Original file line numberDiff line numberDiff line change
@@ -214,10 +214,11 @@ Unlike the exec(3) POSIX system call, `child_process.exec()` does not replace
214214
the existing process and uses a shell to execute the command.
215215

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

222223
```js
223224
const util = require('util');
@@ -295,9 +296,10 @@ stderr output. If `encoding` is `'buffer'`, or an unrecognized character
295296
encoding, `Buffer` objects will be passed to the callback instead.
296297

297298
If this method is invoked as its [`util.promisify()`][]ed version, it returns
298-
a `Promise` for an `Object` with `stdout` and `stderr` properties. In case of an
299-
error (including any error resulting in an exit code other than 0), a rejected
300-
promise is returned, with the same `error` object given in the
299+
a `Promise` for an `Object` with `stdout` and `stderr` properties. The returned
300+
`ChildProcess` instance is attached to the `Promise` as a `child` property. In
301+
case of an error (including any error resulting in an exit code other than 0), a
302+
rejected promise is returned, with the same `error` object given in the
301303
callback, but with an additional two properties `stdout` and `stderr`.
302304

303305
```js

lib/child_process.js

+17-10
Original file line numberDiff line numberDiff line change
@@ -149,17 +149,24 @@ function exec(command, options, callback) {
149149

150150
const customPromiseExecFunction = (orig) => {
151151
return (...args) => {
152-
return new Promise((resolve, reject) => {
153-
orig(...args, (err, stdout, stderr) => {
154-
if (err !== null) {
155-
err.stdout = stdout;
156-
err.stderr = stderr;
157-
reject(err);
158-
} else {
159-
resolve({ stdout, stderr });
160-
}
161-
});
152+
let resolve;
153+
let reject;
154+
const promise = new Promise((res, rej) => {
155+
resolve = res;
156+
reject = rej;
162157
});
158+
159+
promise.child = orig(...args, (err, stdout, stderr) => {
160+
if (err !== null) {
161+
err.stdout = stdout;
162+
err.stderr = stderr;
163+
reject(err);
164+
} else {
165+
resolve({ stdout, stderr });
166+
}
167+
});
168+
169+
return promise;
163170
};
164171
};
165172

test/parallel/test-child-process-promisified.js

+16-4
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,37 @@ const exec = promisify(child_process.exec);
88
const execFile = promisify(child_process.execFile);
99

1010
{
11-
exec(`${process.execPath} -p 42`).then(common.mustCall((obj) => {
11+
const promise = exec(`${process.execPath} -p 42`);
12+
13+
assert(promise.child instanceof child_process.ChildProcess);
14+
promise.then(common.mustCall((obj) => {
1215
assert.deepStrictEqual(obj, { stdout: '42\n', stderr: '' });
1316
}));
1417
}
1518

1619
{
17-
execFile(process.execPath, ['-p', '42']).then(common.mustCall((obj) => {
20+
const promise = execFile(process.execPath, ['-p', '42']);
21+
22+
assert(promise.child instanceof child_process.ChildProcess);
23+
promise.then(common.mustCall((obj) => {
1824
assert.deepStrictEqual(obj, { stdout: '42\n', stderr: '' });
1925
}));
2026
}
2127

2228
{
23-
exec('doesntexist').catch(common.mustCall((err) => {
29+
const promise = exec('doesntexist');
30+
31+
assert(promise.child instanceof child_process.ChildProcess);
32+
promise.catch(common.mustCall((err) => {
2433
assert(err.message.includes('doesntexist'));
2534
}));
2635
}
2736

2837
{
29-
execFile('doesntexist', ['-p', '42']).catch(common.mustCall((err) => {
38+
const promise = execFile('doesntexist', ['-p', '42']);
39+
40+
assert(promise.child instanceof child_process.ChildProcess);
41+
promise.catch(common.mustCall((err) => {
3042
assert(err.message.includes('doesntexist'));
3143
}));
3244
}

0 commit comments

Comments
 (0)