2
const subprocess = require("child_process").spawn("bad_command");
subprocess.on("error", _ => {
    console.info("Failed to spawn a subprocess.");
});
> node test.js
Failed to spawn a subprocess.

Construct one, and then listen to it.

While this is considered as the right way to listen to listenables(socket.io sockets do it, too.) in Node.js (or perhaps Javascript) and I know it would work with no problems, still, I have a slight doubt in my mind: “for what reason does it never miss any events out?


My understanding was that events may asynchronously happen at any time from the creation of the emitter. So I thought like “Why not happening before listening (.on) to them? What exactly ensures that there won’t be any events happening before listening, before calling the .on method?”

Having two more experiments,

const subprocess = require("child_process").spawn("bad_command");
setTimeout(_ => subprocess.on("error", _ => {
    console.info("Failed to spawn a subprocess.");
}), 10);
> node test.js
events.js:180
      throw er; // Unhandled 'error' event
      ^

Error: spawn bad_command ENOENT
    at Process.ChildProcess._handle.onexit (internal/child_process.js:264:19)
    at onErrorNT (internal/child_process.js:456:16)
    at processTicksAndRejections (internal/process/task_queues.js:77:11)
Emitted 'error' event at:
    at Process.ChildProcess._handle.onexit (internal/child_process.js:270:12)
    at onErrorNT (internal/child_process.js:456:16)
    at processTicksAndRejections (internal/process/task_queues.js:77:11) {
  errno: 'ENOENT',
  code: 'ENOENT',
  syscall: 'spawn bad_command',
  path: 'bad_command',
  spawnargs: []
}
const subprocess = require("child_process").spawn("bad_command");
(function heavy_synchronous_task_that_takes_much_longer() {
    const Crypto = require("crypto");
    const buffer = Buffer.alloc(1_000_000);
    console.log(Date.now());
    for(let i = 0; i < 10_000; ++i) {
        Crypto.randomFillSync(buffer);
    }
    console.log(Date.now());
})();
subprocess.on("error", _ => {
    console.info("Failed to spawn a subprocess.");
});
> node test.js
1562646732809
1562646744631
Failed to spawn a subprocess.

I found there is something that ensures it, only if listener-attaching is synchronous (Timing doesn’t matter I guess.), but I cannot grasp what’s going on under the hood because the code itself does not tell me anything about the why.

I believe one can give me a good language-specification–wise, or engine-implementation–wise explanation.

  • Have a look at [this](https://stackoverflow.com/q/30906228/1048572) or [that](https://stackoverflow.com/q/48794320/1048572) – Bergi Jul 09 '19 at 08:48

1 Answers1

1

The error event is emitted on process exit, which is handled by the "nextTick" function (see line 1140 here).

The nextTick, if I'm reading the event loop documentation correctly, allows us to "by using process.nextTick() we guarantee that [...] always runs its callback after the rest of the user's code and before the event loop is allowed to proceed."

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
pkExec
  • 1,752
  • 1
  • 20
  • 39