NodeJS run command when script is terminated - node.js

If this is possible I want to execute some command or script when one npm script is killed with CTRL + C. For example if gulp watch is interrupted.
It is possible?

The exit event is emitted when the process is about to exit. There is no way to prevent the exiting of the event loop at this point, and once all 'exit' listeners have finished running the process will exit. Therefore you must only perform synchronous operations in this handler.
From Node.js process docs
To spawn some command synchronously, you could look into Synchronous Process Creation:
process.on('exit', code => {
require('child_process').spawnSync(...);
require('child_process').execSync(...);
require('child_process').execFileSync(...);
});

If you have a custom script that you run through npm you can listen to the exit event of the process or child process. e.g.
process.on('exit', () => {
console.log('I am exiting....');
});
More info here

Related

Descendent of a child process not receiving SIGTERM on ubuntu but receives on mac

So I have some code which runs a command in a spawned child process. I do this using the execa module.
const childProcess = execa.command('yarn start');
const localhostStarted = await waitForLocalhost({ port: 8000 });
expect(localhostStarted.done).toBe(true);
childProcess.kill('SIGINT', { forceKillAfterTimeout: 2000 });
The yarn start command executes webpack-dev-server in another child process of its own. However when I kill the childProcess that I spawned, it does not automatically kill its spawned webpack-dev-server process. It is known to be an issue here https://github.com/webpack/webpack-dev-server/issues/2168.
To fix this I add manual listeners for SIGINT & SIGTERM inside my script which runs when yarn start is called
['SIGINT', 'SIGTERM'].forEach((signal) => {
console.log('registering events');
process.on(signal, () => {
console.log('received signal', signal);
devServer.close(() => {
console.log('exiting proces');
process.exit(0);
});
});
});
This fixes the issue on my local machine and when I kill child process I spawn, it kills all its descendents i.e the dev-server process too.
However, this fix still does not work on CI, and since the child process gets killed on CI but not the dev-server process, my tests dont exit and keeps hanging.
My local machine is OSX 10.15 but on CI we use ubuntu. If I change CI to use macos 10.15, then the fix works on CI too.
I am unable to find any docs on this issue which explains the different behaviour on why the signal of SIGTERM is not received by the dev-server process on ubuntu machines but receives fine on mac machines.

How to REALLY kill a child_process nodejs

I'm using mocha with Nodejs to test my restApi.
When I run mocha, I tell my test to create a child_process and run the API so I can make requests to it.
The problem is whenever the test exits (finishing or crashing), it seems that the API keeps running on background. I've seen some answers here that instructs to manually kill the child process whenever the main process exits. So I did it like this:
export function startProcess(done) {
const child = spawn('babel-node', ["app.js"]);
child.stdout.on("data", function(data) {
data = data.toString();
// console.log(data.toString());
if(data.indexOf("Server online") > -1) done();
});
child.stderr.on('data', function(err) {
console.log("ERROR: ", err.toString());
});
child.on('exit', function(code) {
console.log("PROPERLY EXITING");
console.log("Child process exited with code", code);
});
process.on('exit', function(code) {
console.log("Killing child process");
child.kill();
console.log("Main process exited with code", code);
});
}
When the main process exits it does log "Killing child process", meaning that child.kill() was indeed called. But if I try to run my test again, when the spawn command gets called, the API throws an error
Error: listen EADDRINUSE :::3300
, meaning that the API is still running and that port address is taken.
So I have to run sudo pkill node to really kill all node process and then npm test works again.
Am I missing something? Is this really the way to achieve what I'm expecting?
I thought about using child_process.exec to run sudo pkill node on my process.on('exit') listener, but that doesnt seem like a smart thing to do.
This is happening both in Mac and Ubuntu.
Any suggestions?
Thanks
"exit" is an event that gets triggered when node finishes it's event loop internally, it's not triggered when you terminate the process externally.
What you're looking for is executing something on a SIGINT.
Have a look at http://nodejs.org/api/process.html#process_signal_events

node.js forever daemon, how to exit properly?

When using forever to run a node.js program as a daemon
i.e.
forever start myNodeTask
If the daemon (myNodeTask) decides it needs to exit, what is the correct way to do so?
If I just call process.exit() the program does terminate but it doesn't delete the forever log file, which leads me to believe that I need the program to exit in a more forever-friendly manner.
The node tasks I'm running are plain tcp servers that stream data to connected clients, not web servers.
The forever module always keeps the log files, even after a process has finished. There is no forever-friendly manner to delete those files.
But, you could use the forever-monitor module, which allow you to programatically use forever (from the docs):
var forever = require('forever-monitor'),
fs = require('fs');
var child = new (forever.Monitor)('your-filename.js', {
max: 3,
silent: true,
options: []
});
child.on('exit', function () {
console.log('your-filename.js has exited after 3 restarts');
// here you can delete your log file
fs.unlink('path_to_your_log_file', function (err) {
// do something amazing
});
});
child.start();

Running Node app through Grunt

I am trying to run my Node application as a Grunt task. I need to spawn this as a child process, however, to allow me to run the watch task in parallel.
This works:
grunt.registerTask('start', function () {
grunt.util.spawn(
{ cmd: 'node'
, args: ['app.js']
})
grunt.task.run('watch:app')
})
However, when changes are detected by the watch task, this will trigger the start task again. Before I spawn another child process of my Node app, I need to kill the previous one.
I can't figure out how to kill the process, however. Something like this does not work:
var child
grunt.registerTask('start', function () {
if (child) child.kill()
child = grunt.util.spawn(
{ cmd: 'node'
, args: ['app.js']
})
grunt.task.run('watch:app')
})
It appears that:
Even though I store the spawned process in a variable outside of the function context, it does not persist, so the next time the start task is run, child is undefined.
child has no kill function…
Take a look at grunt-nodemon which handles a lot of the headaches related to spawning a child process.
This is because grunt-contrib-watch currently spawns all task runs as child processes. So the variable child is not within the same process context. Fairly soon, grunt-contrib-watch#0.3.0 will be released with a nospawn option. This will let you configure the watch to spawn task runs within the same context and would make your above example work.
Take a look at this issue for a little more information:
https://github.com/gruntjs/grunt-contrib-watch/issues/45

nodejs shutdown hook on killall

I'm trying to bind some shutdown function to my nodejs application (version 0.8.12). Since I'm spawning A LOT of child processes and working in a distributed evironment, I'm currently killing the application through
var n = spawn('killall', ['node']);
The problem is that with this apparently the on('exit', ...) logic is no longer working, indeed I have something like this:
process.on('exit', function() {
if(exit_cb)
exit_cb()
else
console.log('About to exit.');
});
And it's not firing whenever I kill the application.
Is there a way to add a shutdown hook with a killall command or should I find another way to kill my child processes in order to have the hook working?
Thanks
You need to listen for SIGTERM signal that killall sends by dafault. But also you need to stop your process manually after all jobs was finished.
process.on('SIGTERM', function() {
if(exit_cb) {
exit_cb();
process.exit();
} else {
console.log('About to exit.');
process.exit();
}
});

Resources