NodeJS fork() method explained

Understand how the NodeJS fork() method works and when you need to use it

Posted on June 14, 2021


Although NodeJS is a single-threaded JavaScript runtime, it can create multiple subprocesses that run separately from each other, allowing you to divide and run your application script from different NodeJS instances.

The child process will have its own memory and runtime instance, so you need to have additional resources to allocate for each child process.

The NodeJS fork() method is a built-in function of the child_process module that allows you to create a child process that’s connected to the main process currently running your code.

The fork() method accepts the following three parameters:

  • A module path string for a JavaScript file to execute on the child process (required)
  • An array of string to pass as the child process arguments
  • The options object that you want to pass to the child process

Of the three parameters, only the first parameter is required for the fork() method to work properly:

fork("file.js", ["argument"], { cwd: process.cwd() });

Let’s learn how the fork() method works in the next section.

NodeJS fork() method in action

You need to create a new JavaScript file named child.js with the following content:

console.log("Hello from child.js");

The code above will be enough to let you know when the child.js script is executed.

Then, create a file named main.js that calls on the child.js file using the fork() method. Don’t forget to require() the child_process module as follows:

const { fork } = require("child_process");

console.log("Running main.js");
console.log("Forking a new subprocess....");

const child = fork("child.js");

Finally, add the event listener method child.on() and listen for the close event so that you will know when the child process has finished running:

const { fork } = require("child_process");

console.log("Running main.js");
console.log("Forking a new subprocess....");

const child = fork("child.js");

child.on("close", function (code) {
  console.log("child process exited with code " + code);
});

Now you need to run the main.js file by using node main command from the shell. The result should be as shown below:

$ node main
Running main.js
Forking a new subprocess....
Hello from child.js
child process exited with code 0

As you can see from the shell output above, the main.js file uses the fork() method to create a child process that runs the child.js script.

The child process created using the fork() method runs asynchronously, which means NodeJS will continue to execute the code below the fork() method without waiting for the child process to finish.

As an example, let’s update your child.js file and use the setTimeout() method to delay the console.log() function from writing to the shell:

// child.js content
setTimeout(function () {
  console.log("Hello from child.js");
}, 2000);

Next, get back to main.js and add the following highlighted code:

const { fork } = require("child_process");

console.log("Running main.js");
console.log("Forking a new subprocess....");

const child = fork("child.js");

console.log("main process is still running...");

child.on("close", function (code) {
  console.log("child process exited with code " + code);
});

Now run the main.js file again. Notice how the log below the fork() method is executed before the child.js execution is finished:

$ node main
Running main.js
Forking a new subprocess....
main process is still running...
Hello from child.js
child process exited with code 0

The asynchronous nature of NodeJS child process allows you to create multiple instances that execute different tasks without waiting for each other.

fork() method communication channel

The fork() method also establishes a communication channel between the main process and its child process, allowing you to exchange information between the two processes.

To send a message to a child process, you can use the child.send() method as shown below:

// main.js
const { fork } = require("child_process");

console.log("Running main.js");
console.log("Forking a new subprocess....");

const child = fork("child.js");
child.send(29);

Then the child process script can accept the message by listening to NodeJS global process.on() method as follows:

// child.js
process.on("message", function (message) {
  console.log(`Message from main.js: ${message}`);
});

The child process script can send a message back to the main process by using the process.send() method as follows:

// child.js
process.on("message", function (message) {
  console.log(`Message from main.js: ${message}`);
});

process.send("Nathan");

The main process can accept the message from its child process by listening to the message event, similar to the close event above:

// main.js
const { fork } = require("child_process");

console.log("Running main.js");
console.log("Forking a new subprocess....");

const child = fork("child.js");
child.send(29);

child.on("message", function (message) {
  console.log(`Message from child.js: ${message}`);
});

Now when you run the node main command, the shell output will be as follows:

Running main.js
Forking a new subprocess....
Message from child.js: Nathan
Message from main.js: 29

Notice how the message from main.js is logged after the message from child.js. This is because child.js is executed separately from the main process. It just happens to send the message faster than the main process.

If you want to send the message only after the main process has finished sending its message, you need to put the process.send() method inside the process.on() listener:

process.on("message", function (message) {
  console.log(`Message from main.js: ${message}`);
  process.send("Nathan");
});

This way, the output will be sequential:

Running main.js
Forking a new subprocess....
Message from main.js: 29
Message from child.js: Nathan

Next, let’s look at how to send arguments to the child process.

Passing arguments to fork() method child process

The array passed as the second argument to the fork() method will be accepted by the child process as command-line arguments, which you can retrieve using the process.argv() property.

Here’s an example of sending 2 arguments to the child process:

const child = fork("child.js", [99, "Nathan"]);

Then inside the child.js file, retrieve the arguments using the process.argv property:

console.log(process.argv[2]); // "99"
console.log(process.argv[3]); // "Nathan"

Keep in mind that command-line arguments are always converted to string type.

Passing options to fork() method child process

Finally, you can also pass the options object to your child process by as the third argument to the fork() method.

One example of using the option object is to pass the AbortController instance so that the main process can abort the child process as follows:

const { fork } = require("child_process");
const controller = new AbortController();
const { signal } = controller;

const child = fork("child.js", [], { signal });
child.on("error", (err) => {
  // This will be called with err being an AbortError if the controller aborts
  console.log(err);
});
controller.abort(); // Stops the child process

The options available for the fork() method allows you to influence the child process in some ways.

For a complete list of fork() method options, you can refer to the NodeJS fork() method documentation

Now you’ve learned how the fork() method works. Congratulations! 😉

Related articles:

Level up your programming skills

I'm sending out an email once every Wednesday with the latest programming tutorials. Drop your email in the box below and I'll send new stuff straight into your inbox!

No spam. Unsubscribe anytime.