How JavaScript works: building a child process in Node.js

Victor Jonah
SessionStack Blog
Published in
8 min readDec 10, 2021

This is post # 55 of the series, dedicated to exploring JavaScript and its building components. In the process of identifying and describing the core elements, we also share some rules of thumb we use when building SessionStack, a JavaScript tool for developers to identify, visualize, and reproduce web app bugs through pixel-perfect session replay.

Ever run into an issue where a request to the server is blocked by an ongoing process? And new requests have to wait for their turn. Some requests might be expensive and take time to process which will make your user wait and even leave frustrated. This happens when you have only a single thread for execution.

The child process is a process that is a sub process or created by the main process. This idea relates to having concurrent tasks running in a way that a task does not have to wait for its turn when another task is still executing.

Like we said earlier, some expensive requests create a delay to your web server by hitting computationally slow endpoints or maybe having a high number of requests. For computationally slow endpoints, creating a child process is how we could scale the server.

In this article, we will employ one of the architectures Node.js uses to avoid a server request that is being delayed. This way an incoming request is forked into its child process for execution leaving the main process free. This is still referred to as multithreading.

This is going to be a concise article and basic knowledge of Node.js is going to be needed.

What is a Child Process?

Node.js runs on a single thread and a server request is being executed on a process. This process in particular is the main process and whatever requests from the client come in is being acted on the main process. But Node.js also provides child processes where it is being created by the main process. One particular reason for that child process is avoiding the main server from being blocked by anything.

Why a Child Process?

Running a single thread in the CPU with a heavy workload is not efficient in Node.js, the reason being there will be a delay. Take for example an API endpoint that is doing something very expensive like image processing, some deep algorithms, etc. This will hold up further requests coming in, which we do not want. This is a big issue.

The best thing to do to fix this is to create a child process; there are few ways to create one but our focus will be using the fork() method.

Buildings non-blocking web server

Let’s jump right quickly and demonstrate how this works in code. First, we are going to create a web server having just one endpoint. This endpoint will be calculating prime numbers. This means that we give it a number and it tells us if it is a prime number or not.

At some point, a user might input a large number and the request might take a long time to process thereby blocking more inputs from the user.

Create a folder and name it primeNumber or whatever you like. Open your terminal and move into the newly created folder:

The command above will create a package.json file for us in our folder.

We also create an index.js file which will have our function to calculate a prime number and hold our server configuration:

Below is our response when we trigger the endpoint on Postman. The time to calculate the prime number is 2 milliseconds.

If we try a larger number, the time will increase significantly. Let’s try that out.

So, we tried the number 29355126551 which will take a long time to calculate, probably some minutes. But if we try another request to this same endpoint, that endpoint is blocked until the initial calculation or process is done. An example is below:

The number we are calculating is 39355123552 here. The idea is that this request will have to be pending until the initial calculation is done. This is why Node.js provides us with a child process so that every incoming request can be handled by cloning the main process to calculate the prime number instead of having to wait. This is the best we can do to scale for expensive endpoints.

Let’s look at how we can implement this. The first thing to note is that our endpoint remains the same. The only difference is that incoming request to that endpoint is given a process to sort the calculation.

So, create a file called calculatePrime.js or whatever you want. This file contains the function to calculate the prime number.

Back to our index.js file, we will need to import a module that Node.js provides to us which is called child_process. What this module does for us is quite simple, it provides a fork method that will be used to clone the current process:

What we have done is to create a child process that will execute the file (this file is our function). The childProcess is now an object which has a communication method. This communication method is what will be used to send messages between the parent and child process:

Watch closely. We use the childProcess.send() method to send the number to the function (the function that calculates for a prime number). childProcess.on() is a listener that listens to the child process just in case a response is sent back to the parent process.

Going to the ./calculatePrime.mjs file, we need to listen to the parent process from there. Very simple:

We listen to the parent process for the message, pass it to our function, get the response back from the function and kill or exit the process. We are killing the process since we are not going to be using it again and also need to do so to avoid the process of eating our CPU because more processes are being created for each request.

What is next? Nothing. Below is the full code:

This way every single request is being processed in the child process and their responses are sent back to the parent process.

Pros

Let us look at some of the benefits of spawning a child process in your application. The very noticeable one is that it helps avoid server delays caused by a complex endpoint. That way your application server is not blocked, more requests can be taken and it is scaled properly.

Cons

Managing a lot of these processes becomes very difficult to handle which requires more advanced code to handle them. Another Node.js module called cluster comes into play here. The best you can do is try to avoid having to spin up child processes.

Another problem with the child process is that it is not easy to debug. For example, if a particular process has a bug, how do you know what process exactly? How do you know the process ID so you can step into it and debug it? This becomes a problem to start the debugging process.

I must have deliberately skipped that there are various ways of creating a child process in Node.js. Yes, there are but using the fork() method handles expensive requests. This is one method that can scale your application server. Other ways include the spawn(), exec(), and execFile().

Even if you feel like the scalability decisions have been made, it’s always necessary to verify that this is indeed true and your users have a great experience with your product. Having slow network response and page loading will make users leave without a doubt.

A solution like SessionStack will help you determine and further optimize the experience of your users by allowing you to replay their journeys as videos, showing you how your users actually experience your product. You can quickly determine whether your product is performing according to their expectations or not. In case you see that something is wrong, you can explore all of the technical details from the user’s browser such as the network, debug information, and everything about their environment so that you can easily understand the problem and resolve it.

There is a free trial if you’d like to give SessionStack a try.

SessionStack replaying a session

If you missed the previous chapters of the series, you can find them here:

--

--