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: https://github.com/nodejs/node/issues/35288
PR-URL: https://github.com/nodejs/node/pull/35369
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
This commit is contained in:
Matthew Francis Brunetti 2020-09-27 15:00:07 -04:00 committed by Gireesh Punathil
parent 923f76d523
commit 3df5afb367
4 changed files with 51 additions and 0 deletions

View File

@ -1036,6 +1036,21 @@ child process, the `message` argument can contain data that JSON is not able
to represent.
See [Advanced serialization][] for more details.
### Event: `'spawn'`
<!-- YAML
added: REPLACEME
-->
The `'spawn'` event is emitted once the child process has spawned successfully.
If emitted, the `'spawn'` event comes before all other events and before any
data is received via `stdout` or `stderr`.
The `'spawn'` event will fire regardless of whether an error occurs **within**
the spawned process. For example, if `bash some-command` spawns successfully,
the `'spawn'` event will fire, though `bash` may fail to spawn `some-command`.
This caveat also applies when using `{ shell: true }`.
### `subprocess.channel`
<!-- YAML
added: v7.1.0

View File

@ -401,6 +401,8 @@ ChildProcess.prototype.spawn = function(options) {
this._handle.close();
this._handle = null;
throw errnoException(err, 'spawn');
} else {
process.nextTick(onSpawnNT, this);
}
this.pid = this._handle.pid;
@ -466,6 +468,11 @@ function onErrorNT(self, err) {
}
function onSpawnNT(self) {
self.emit('spawn');
}
ChildProcess.prototype.kill = function(sig) {
const signal = sig === 0 ? sig :

View File

@ -41,6 +41,8 @@ assert.strictEqual(enoentChild.stdio[0], enoentChild.stdin);
assert.strictEqual(enoentChild.stdio[1], enoentChild.stdout);
assert.strictEqual(enoentChild.stdio[2], enoentChild.stderr);
enoentChild.on('spawn', common.mustNotCall());
enoentChild.on('error', common.mustCall(function(err) {
assert.strictEqual(err.code, 'ENOENT');
assert.strictEqual(getSystemErrorName(err.errno), 'ENOENT');

View File

@ -0,0 +1,27 @@
'use strict';
const common = require('../common');
const spawn = require('child_process').spawn;
const assert = require('assert');
const subprocess = spawn('echo', ['ok']);
let didSpawn = false;
subprocess.on('spawn', function() {
didSpawn = true;
});
function mustCallAfterSpawn() {
return common.mustCall(function() {
assert.ok(didSpawn);
});
}
subprocess.on('error', common.mustNotCall());
subprocess.on('spawn', common.mustCall());
subprocess.stdout.on('data', mustCallAfterSpawn());
subprocess.stdout.on('end', mustCallAfterSpawn());
subprocess.stdout.on('close', mustCallAfterSpawn());
subprocess.stderr.on('data', common.mustNotCall());
subprocess.stderr.on('end', mustCallAfterSpawn());
subprocess.stderr.on('close', mustCallAfterSpawn());
subprocess.on('exit', mustCallAfterSpawn());
subprocess.on('close', mustCallAfterSpawn());