web-development top banner

What is Node JS Process?

Read it in 8 Mins

Last updated on
22nd Mar, 2021
Published
03rd Feb, 2021
Views
3,570
What is Node JS Process?

How to use the global process module

Node.js is built using C++ program with a V8 engine embedded in it to provide an environment to execute JavaScript outside the browser. Many of the accessible global methods are actually wrappers around methods which make calls to core C libraries.

Node.js has many in-built global identifiers that are available and known to developers. There are also some which are accessible at the module level via module inheritance.

Few of the global objects are:

  • global - It is a namespace and accessible across the application. Setting a property to this namespace makes it accessible throughout the running process.
  • process - Process is a module which provides interaction with the current Node.js process.
  • console - Console is a module mostly used for logging the information or error. It wraps the STDIO functionality of a process.
  • setTimeout(), clearTimeout(), setInterval(), clearInterval() - All these can be categorized as timer functions.

Some of the globules accessible via module inheritance are module, exports, __filename, __dirname, require() etc.

In this article, we attempt to understand more about the ‘process’ object and its details with examples. ‘process’ object is a global which is an instance of EventEmitter and can be accessed directly. It can also be explicitly accessed using module global i.e. require.

const process = require(‘process’);

process object has a property ‘argv’ which is an array containing the properties passed to node process.

Create a simple index.js file and lets console.log the process.argv

console.log(process.argv) 

Type in ‘node index.js’ in terminal. On pressing Enter, the following output should be provided:

[
  '/Users/***/.nvm/versions/node/v12.20.1/bin/node',
  '/Users/***/index.js'
]

Now, let’s pass in some other parameters and you should see the parameters being displayed say ‘node index.js test’.
Also note that process has ‘process.stdout’ and ‘process.stderr’ which helps us to send a message to the standard output channel and standard error channel if there is any error.
Infact, the console.log is internally doing process.stdout.write(msg + ‘\n’).
console.log(‘Hello world’) is the same as process.stdout.write(‘Hello world’ + ‘\n’).
Files can be read as stream and we can pipe this to process.stdout. For example, replace the content of index.js with the below code.

var fs = require('fs')
fs.createReadStream(__filename).pipe(process.stdout);

Running the ‘node index.js’ should display the content of the file. 

Another interesting thing about Node.js is that when it is done or doesn’t have anything left to do, then it will exit out of the process. Let’s understand this with an example.

setTimeout(() => { 
process.stdout.write('Executed after 1000 ms' + '\n'); 
}, 1000)

This one will wait for 1 sec and then outputs ‘Executed after 1000 ms’ and terminates the process.

If we want the process to run forever, then we can replace the setTimeout with setInterval which will execute the callback every time, post the time interval. And the only way to exit is by pressing ‘Ctrl+C’ or the process gets crashed.

To get a quick walk through of properties, methods and events on the process object, add ‘console.log(process)’ in index.js and run node index.js. Most of them are self-explanatory as per their name.

version: 'v12.20.1', //current version of node 
versions: {…}, // gives insight about the node and its core components like V8 engine version 
arch: 'x64', 
platform: 'darwin', 
release: {…}, // details of node source and version of lts. 
moduleLoadList: [...], // details of modules available with node. 
binding: [Function: binding], 
_events: [Object: null prototype] {  
newListener: [Function: startListeningIfSignal], // whenever a new listener is added  
removeListener: [Function: stopListeningIfSignal], // existing listener is removed  
warning: [Function: onWarning],  
 SIGWINCH: [Function]  
},  
_eventsCount: 4,  
_maxListeners: undefined,  
domain: null,  
_exiting: false,  
config: {  
target_defaults: {…},  
variables: {...}  
},  
cpuUsage: [Function: cpuUsage],  
resourceUsage: [Function: resourceUsage],  
memoryUsage: [Function: memoryUsage],  
kill: [Function: kill],  
exit: [Function: exit],  
openStdin: [Function],  
getuid: [Function: getuid],  
geteuid: [Function: geteuid],  
getgid: [Function: getgid],  
getegid: [Function: getegid],  
getgroups: [Function: getgroups],  
allowedNodeEnvironmentFlags: [Getter/Setter],  
assert: [Function: deprecated],  
features: {…},  
setUncaughtExceptionCaptureCallback: [Function: setUncaughtExceptionCaptureCallback],  
hasUncaughtExceptionCaptureCallback: [Function: hasUncaughtExceptionCaptureCallback],  
emitWarning: [Function: emitWarning],  
nextTick: [Function: nextTick],  
stdout: [Getter],  
stdin: [Getter],  
stderr: [Getter],  
abort: [Function: abort],  
umask: [Function: wrappedUmask],  
chdir: [Function: wrappedChdir],  
cwd: [Function: wrappedCwd],  
initgroups: [Function: initgroups],  
setgroups: [Function: setgroups],  
setegid: [Function],  
seteuid: [Function], 
setgid: [Function], 
setuid: [Function], 
env: {…}, // environment details for the node application 
title: 'node', 
argv: [  
   '/Users/srgada/.nvm/versions/node/v12.20.1/bin/node', 
    '/Users/srgada/index.js' 
 ], 
execArgv: [], 
pid: 29708, 
ppid: 19496, 
execPath: '/Users/srgada/.nvm/versions/node/v12.20.1/bin/node', 
debugPort: 9229, 
argv0: 'node', 
mainModule: Module {…} //details of the main starting file or module. This is deprecated in latest one and use require.main instead
 }

Let’s take a look at a few of the properties which are most used or required.

  • pid – gives the process id 
  • platform – is linux or darwin 
  • version – node version 
  • title – process name, by default it is node and can be changed 
  • execPath – for executable process path 
  • argv – arguments passed

Some common methods are:

  • exit - exits the process and accepts the exit code as argument. 
  • cwd – to get the current working directory and to change we can use ‘chdir’. 
  • nextTick – as the name suggests, it places the callback passed to this function in the next iteration of event loop. It is different to setTimeout with 0 ms delay.
process.nextTick(() => {  
   console.log('Got triggered in the next iteration of event loop');  
});  
setTimeout(() => {  
   console.log("Even after nextTick is executed");  
}, 0);  
console.log("First text to be printed"); 

Output: 

First text to be printed  
Got triggered in the next iteration of event loop  
Executed after some delay

EVENTS: 
To log (or) perform any cleaning before exiting the process, we can hook to ‘exit’ event which is raised when process.exit is invoked.

console.log(process.argv);
process.on('exit', () => {  
   console.log('Perform any clean up like saving or releasing any memory');  
});

Exit is fired after the event loop is terminated. As a result, we can’t perform any async work in the handler. So if you want to perform some calls like saving content to db, we can hook to ‘beforeExit’ when the process gets exited.

process.on('beforeExit', code => {
    // Can make asynchronous calls
    setTimeout(() => {
      console.log(`Process will exit with code: ${code}`)
      process.exit(code)
    }, 1000)
  });
  process.on('exit', code => {
    // Only synchronous calls
    console.log(`Process exited with code: ${code}`)
  });  
  console.log('After this, process will try to exit');

Another event ‘uncaughtException’, as the name suggests, is raised when there is an unhandled exception in the application. Whenever an unhandled exception is found, the Node.js application logs the stack raise and exit.

process.on('exit', () => {  
    console.log('Perform any clean up like saving or releasing any memory');  
});  
process.on('uncaughtException', (err) => {  
    console.error('An unhandled exception is raised. Look at stack for more details');  
    console.error(err.stack);
    process.exit(1);
});
var test = {};  
//raises an exception.  
test.unKnownObject.toString();

Output

An unhandled exception is raised. Look at stack for more details
TypeError: Cannot read property 'toString' of undefined
at Object.<anonymous> (/Users/srgada/index.js:10:20)
at Module._compile (internal/modules/cjs/loader.js:999:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
at internal/main/run_main_module.js:17:47
Perform any clean up like saving or releasing any memory

Similar to ‘uncaughtException’, a newer concept called unhandledRejection error is introduced. This is raised if a promise is rejected and there is no handler to the response.

In both the cases, it is expected that the application will crash and should not be continued.  One reason could be that the application might be in an undefined state. If you are wondering why someone would hook to this event, then it is to perform synchronous cleanup of allocated resources (e.g. file descriptors, handles, etc) before shutting the process.

Note: ‘beforeExit’ event is not fired when there is an ‘uncaughtException’ or process.exit is called explicitly.

Signal Events: Events emitted by operating system to Node.js process are referred to as signals. Most common among them are SIGTERM and SIGINT. Both are related to process termination. Signals are not available to worker threads. Let’s look into an example for SIGINT:

setInterval(() => {
    console.log('continued process');
 }, 1000);
process.on('SIGINT', signal => {
    console.log(`Process ${process.pid} has been interrupted`)
    process.exit(0)
});

In the terminal, execute node index.js. This will be a continuous process without any exit criteria because of setInterval.  

Press Ctrl+C, the result is that ‘SIGINT’ event is raised to node application and is captured in handler. Because of process.exit command in handler, the process exits.

Node.js is a single thread process. And in some cases, you may want some specific logic to be run in the child process and not in the main one, so that if any crash happens the main process is still alive.

Taking the previous example of displaying the content of the index.js file, let’s do it this time with the help of ‘child_process’ module.

var exec = require('child_process').exec;
exec('cat index.js',(err, stdout, stderr) => {
console.log(stdout);
});

Note: cat is a binary which is available on iOS. This may vary based on your operating system.
 ‘spawn’ on child process is similar to exec but it gives more granular control of how the processes are executed.
Let’s spin off a child process from the parent process and pass on the data from child process to child process.

var spawn = require('child_process').spawn;  
if (process.argv[2] === 'childProcess')  
{  
   console.log('Child process is executed’);  
} else {  
   var child = spawn(process.execPath, [__filename, 'childProcess']);  
   child.stdout.on('data', (data) => {  
      console.log('from child:', data.toString());  
   })  
}

Overview:

  •  At line 6, spawn is provided with the process to execute and second parameter is the parameter passed to it.
  • Since we want to spin off another node process, we rely on ‘process.execPath’.
  • ‘__filename’ is the name of the file i.e. index.js and second argument being ‘childProcess’.
  • When child process is spawned [like node index.js childProcess], condition at line 2 is satisfied and sends out the data to the parent process.
  • Parent process captures the stdout of child on data changed event and prints the same in stdout.

 Output: 

from child: Child process is running

As we learnt earlier, all stdout is a stream and we can pipe the child.stdout to the parent.stdout directly instead of listening to the data changed event. 

Replace line 7,8,9 with this:

child.stdout.pipe(process.stdout);

Another shorthand version is to provide the 3rd parameters to the spawn without the need to pipe as shown below: [it basically inherits allstdio from the parent and does pipe for you]

var child = spawn(process.execPath, [__filename, 'childProcess'], {  
stdio: 'inherit'
});

Note that, each child process is self-contained and data is not shared across multiple child processes or parent.

What if you want to transfer the data or control the child process like terminate if needed?

The third parameter of the spawn containing the stdio is basically an array with stdIn, stdOut and stdErr. Passing null will take the default. We can pass in the fourth item ‘pipe’ which helps in sending the data from child process.

var spawn = require('child_process').spawn; 
if (process.argv[2] === 'childProcess') 
{ 
    var net = require('net'); 
    var pipe = new net.Socket({ fd: 3 }); 
    pipe.write('Terminate me'); 
} else { 
    var child = spawn(process.execPath, [__filename, 'childProcess'], { 
       stdio: [null, null, null, 'pipe'] 
    }); 
    child.stdio[3].on('data', (data) => { 
    if (data.toString() === 'Terminate me') { 
       console.log('Terminated child process'); 
       child.kill(); 
    } 
}); 
}

From the above code snippet, we can see that child creates a socket with parent and sends the data. The parent is listening to it and does the required operation i.e. to terminate the child process.

Conclusion

Node.js is a single-threaded, non-blocking performance and works great for a single process. But what if we want to scale up and have a distributed application? Regardless of how performant the server is, single thread can only support a limited load. To overcome this, Node.js has to work with multiple processes and even on multiple machines.

Profile

Sumanth Reddy

Author

Full stack,UI Architect having 14+ Years of experience in web, desktop and mobile application development with strong Javascript/.Net programming skills .Strong experience in microsoft tech stack and Certified in OCI associate. Go-to-guy on integration of applications, building a mobile app by zeroing on tech stack. Majorily experienced on engineering based IIOT products and Marekting Automation products deployed on premise and on-cloud.