Skip to content

Commit aa5b726

Browse files
zenflowMylesBorins
authored andcommitted
child_process: add ChildProcess 'spawn' event
The new event signals that the subprocess has spawned successfully and no 'error' event will be emitted from failing to spawn. Fixes: #35288 PR-URL: #35369 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
1 parent d1004d2 commit aa5b726

File tree

4 files changed

+51
-0
lines changed

4 files changed

+51
-0
lines changed

doc/api/child_process.md

+15
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,21 @@ child process, the `message` argument can contain data that JSON is not able
10391039
to represent.
10401040
See [Advanced serialization][] for more details.
10411041

1042+
### Event: `'spawn'`
1043+
<!-- YAML
1044+
added: REPLACEME
1045+
-->
1046+
1047+
The `'spawn'` event is emitted once the child process has spawned successfully.
1048+
1049+
If emitted, the `'spawn'` event comes before all other events and before any
1050+
data is received via `stdout` or `stderr`.
1051+
1052+
The `'spawn'` event will fire regardless of whether an error occurs **within**
1053+
the spawned process. For example, if `bash some-command` spawns successfully,
1054+
the `'spawn'` event will fire, though `bash` may fail to spawn `some-command`.
1055+
This caveat also applies when using `{ shell: true }`.
1056+
10421057
### `subprocess.channel`
10431058
<!-- YAML
10441059
added: v7.1.0

lib/internal/child_process.js

+7
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,8 @@ ChildProcess.prototype.spawn = function(options) {
401401
this._handle.close();
402402
this._handle = null;
403403
throw errnoException(err, 'spawn');
404+
} else {
405+
process.nextTick(onSpawnNT, this);
404406
}
405407

406408
this.pid = this._handle.pid;
@@ -466,6 +468,11 @@ function onErrorNT(self, err) {
466468
}
467469

468470

471+
function onSpawnNT(self) {
472+
self.emit('spawn');
473+
}
474+
475+
469476
ChildProcess.prototype.kill = function(sig) {
470477

471478
const signal = sig === 0 ? sig :

test/parallel/test-child-process-spawn-error.js

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ assert.strictEqual(enoentChild.stdio[0], enoentChild.stdin);
4141
assert.strictEqual(enoentChild.stdio[1], enoentChild.stdout);
4242
assert.strictEqual(enoentChild.stdio[2], enoentChild.stderr);
4343

44+
enoentChild.on('spawn', common.mustNotCall());
45+
4446
enoentChild.on('error', common.mustCall(function(err) {
4547
assert.strictEqual(err.code, 'ENOENT');
4648
assert.strictEqual(getSystemErrorName(err.errno), 'ENOENT');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use strict';
2+
const common = require('../common');
3+
const spawn = require('child_process').spawn;
4+
const assert = require('assert');
5+
6+
const subprocess = spawn('echo', ['ok']);
7+
8+
let didSpawn = false;
9+
subprocess.on('spawn', function() {
10+
didSpawn = true;
11+
});
12+
function mustCallAfterSpawn() {
13+
return common.mustCall(function() {
14+
assert.ok(didSpawn);
15+
});
16+
}
17+
18+
subprocess.on('error', common.mustNotCall());
19+
subprocess.on('spawn', common.mustCall());
20+
subprocess.stdout.on('data', mustCallAfterSpawn());
21+
subprocess.stdout.on('end', mustCallAfterSpawn());
22+
subprocess.stdout.on('close', mustCallAfterSpawn());
23+
subprocess.stderr.on('data', common.mustNotCall());
24+
subprocess.stderr.on('end', mustCallAfterSpawn());
25+
subprocess.stderr.on('close', mustCallAfterSpawn());
26+
subprocess.on('exit', mustCallAfterSpawn());
27+
subprocess.on('close', mustCallAfterSpawn());

0 commit comments

Comments
 (0)