You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
That's because node:child_processspawn() is synchronous. However, in our case, this requires some of our own logic to be synchronous so we can return promise.subprocess. This creates the following problems.
Problems
fs.statSync() calls
On Windows, we must make stat syscalls to know whether the binary requires calling cmd.exe or not. We do this twice (since we check two file extensions: .exe and .com) on every directory of the PATH. Due to the above, we must make synchronous calls. That's 20-50 sync stat calls done serially.
Being able to make them asynchronously would be much faster, since we could parallelize those calls.
// This must unfortunately be synchronous because we return the spawned `subprocess` synchronously
returnstatSync(possibleFile).isFile();
}catch{
returnfalse;
}
});
Synchronous exceptions
Even though nanoSpawn() is async, it can throw sometimes synchronously, which is inconsistent. That's because node:child_processspawn() throws synchronously when passing some invalid options, e.g. {uid: -1} or {detached: 'should_be_a_boolean'}.
Early errors
When running a binary that cannot be found (e.g. due to being misspelled, or not being installed), no subprocess is actually spawned at the OS level. However, Node.js returns an "empty" subprocess instance, which is not very useful. It's also tricky, e.g. its subprocess.pid is undefined, leading to potential bugs. Node.js then emits an error event on the subprocess instance, which leads to nanoSpawn() to throw asynchronously.
This situation happens in a couple of other situations, e.g. when spawning too many processes at once, or when hitting OS permissions errors.
Instead of returning a subprocess instance which might be "empty", we should wait for the spawn event first. That event is actually emitted only one tick later.
Output iteration + buffering
Due to the above, we need to spawn the subprocess before knowing whether the user wants to iterate its output line-wise. This leads to the problem described in #33.
Solutions
The subprocess instance is useful for a few purposes such as calling subprocess.kill() or piping subprocess.std*. However, it is meant mostly as an escape hatch, so it's probably ok if it's slightly harder to access.
Using either callbacks (e.g. an onStart(subprocess) option) or events (e.g. a "start" event with a subprocess payload) is possible too, but harder to use in a promise-based codebase.
If you have any other idea on the specific API, please let me know.
What do you think?
The text was updated successfully, but these errors were encountered:
We return the subprocess synchronously, as
promise.subprocess
.nano-spawn/index.js
Line 23 in 5f449e1
nano-spawn/index.js
Line 26 in 5f449e1
nano-spawn/index.js
Lines 30 to 31 in 5f449e1
That's because
node:child_process
spawn()
is synchronous. However, in our case, this requires some of our own logic to be synchronous so we can returnpromise.subprocess
. This creates the following problems.Problems
fs.statSync()
callsOn Windows, we must make
stat
syscalls to know whether the binary requires callingcmd.exe
or not. We do this twice (since we check two file extensions:.exe
and.com
) on every directory of thePATH
. Due to the above, we must make synchronous calls. That's 20-50 syncstat
calls done serially.Being able to make them asynchronously would be much faster, since we could parallelize those calls.
nano-spawn/windows.js
Lines 34 to 41 in 5f449e1
Synchronous exceptions
Even though
nanoSpawn()
is async, it can throw sometimes synchronously, which is inconsistent. That's becausenode:child_process
spawn()
throws synchronously when passing some invalid options, e.g.{uid: -1}
or{detached: 'should_be_a_boolean'}
.Early errors
When running a binary that cannot be found (e.g. due to being misspelled, or not being installed), no subprocess is actually spawned at the OS level. However, Node.js returns an "empty" subprocess instance, which is not very useful. It's also tricky, e.g. its
subprocess.pid
isundefined
, leading to potential bugs. Node.js then emits anerror
event on the subprocess instance, which leads tonanoSpawn()
to throw asynchronously.This situation happens in a couple of other situations, e.g. when spawning too many processes at once, or when hitting OS permissions errors.
Instead of returning a
subprocess
instance which might be "empty", we should wait for thespawn
event first. That event is actually emitted only one tick later.Output iteration + buffering
Due to the above, we need to spawn the subprocess before knowing whether the user wants to iterate its output line-wise. This leads to the problem described in #33.
Solutions
The
subprocess
instance is useful for a few purposes such as callingsubprocess.kill()
or pipingsubprocess.std*
. However, it is meant mostly as an escape hatch, so it's probably ok if it's slightly harder to access.We should make retrieving the
subprocess
instance asynchronous. One possible implementation is:Using either callbacks (e.g. an
onStart(subprocess)
option) or events (e.g. a"start"
event with asubprocess
payload) is possible too, but harder to use in a promise-based codebase.If you have any other idea on the specific API, please let me know.
What do you think?
The text was updated successfully, but these errors were encountered: