DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 968,547 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
kamran
kamran

Posted on

Make your Node JS AppΒ Faster.

Your manager is shouting at you to improve your Node js performance but you are stuck. You either want to quit your job or hit your manager's head with a brick. Please don't do either. In this post I am going to tell you how you can improve your Nodejs application performance.

If you are lazy to read, i also have video for same topic.

Application's performance has always been a challenge for any developer in any programming language.You can define a good developer as a developer who can develop most efficient or best performing applications and in case of web development applications, the more number of I/O the application can handle, the better performant is the application.So we have to build applications which can handle a lot of i/o smoothly and without any delay.

First let's try to understand the potential reason for the lag or the inefficiency. As we know behind the scenes Node js has this awesome event loop which helps nodejs to run multiple tasks at once very smoothly, so how exactly node js slows down.

Event Loop
If we dive deep in event loop phases we see that the poll phase can be the reason for slowness sometimes if the I/O is too big to be executed. Yeah it's possible that if we do some CPU intensive work, the poll phase may take more time and your whole application slows down. It is not a big deal. Large scale applications sometimes face this issue. So let's talk about how we can handle these scenarios in node JS applications.

Poll phase in Event Loop

The best solution which I always recommend and I also personally use is using "Worker thread". Yeah I know that Node JS is a single threaded framework, so we are not going to actually use multithreading over here. Instead of that we are going to spin up some worker threads which will be running on a separate event loop. This new event loop will be completely isolated from the main event loop and any kind of CPU intensive work we are going to offload to this thread. Now let's try to take an example where we can create a new worker thread and we'll try to understand how you can implement the same in your application.Same implementation I have already done in of one of my YouTube videos, so, if you are a video kind of person then you can check out my video on my YouTube channel.

Let's talk about the example, we are going to create a main script where our main thread will be running and we are going to create another script where we are going to do some CPU intensive work and then we are going to run this separate script in a separate worker thread. Let's start step by step how we can create a worker thread and how we can offload our work to that thread.
We will create a main.js in which I am going to listen to a single API and also we are going to create a separate module in which we are going to do do a CPU intensive work which will be calculating a Factorial of a given number as we all know calculating factorials takes up a lot of CPU.

const express = require('express');
const http = require('http');

async function init() {

    const port = process.env.PORT;
    const app = express();

    const httpsServer = http.Server(app);

    app.get('/', (req, res, next) => {
        //do stuffs
        res.json({ message: "All good" })
    });

    httpsServer.listen(port, () => {
        logger.info(`App listening on port ${port}`);
    });

}

init();
Enter fullscreen mode Exit fullscreen mode

Lets create a module for factorial

module.exports = (number) => {
    const getFactorial = (number) => {
        if (number === 1) return 1
        return number * getFactorial(number - 1);
    }
    return getFactorial(number);
}
Enter fullscreen mode Exit fullscreen mode

Now the traditional method is we import factorial js in main js and use it in the api response

const getFactorial = require('./factorial');
app.get('/', (req, res, next) => {
    const number = req.query.number;
    res.json({ message: "All good" });
});
Enter fullscreen mode Exit fullscreen mode

In this case when numbers are bigger, the process will take up time and block other processes, and that's what we don't want. So we will create a worker thread.

Lets create a new worker thread and do calculations in that, creating a worker is simple, You have to create a separate file for the worker and use that file to create a worker instance in any file.

const { Worker } = require('worker_threads');
const worker = new Worker('myworkerthread.js');
Enter fullscreen mode Exit fullscreen mode

Your worker file will be simple script where direct logic will be written.

const getFactorial = (number) => {
    if (number === 1) return 1
    return number * getFactorial(number - 1);
}
getFactorial(number);
Enter fullscreen mode Exit fullscreen mode

Now if we want to share data between this worker thread and the main thread, we can share data by emitting messages.
To emit messages to worker thread from parent thread

worker.postMessage(message);
Enter fullscreen mode Exit fullscreen mode

To emit messages to parent thread from worker thread

const { parentPort } = require('worker_threads');
parentPort.postMessage(message);
Enter fullscreen mode Exit fullscreen mode

You have to listen for parent Messages in the worker thread and for worker messages in parent thread. We have dedicated ports to listen to these emitted messages.
To listen worker message in parent thread

worker.on('message', (message) => {
    console.log("Worker Message", message);
});
Enter fullscreen mode Exit fullscreen mode

To listen parent message in worker thread

const { parentPort } = require('worker_threads');
parentPort.on('message', (message) => {
    console.log("Parent Message", message);
});
Enter fullscreen mode Exit fullscreen mode

Finally your main.js will look like

const express = require('express');
const http = require('http');
const { Worker } = require('worker_threads');
async function init() {
    const port = process.env.PORT;
    const app = express();
    const factorialworker = new Worker('./factorial.js');
    const httpsServer = http.Server(app);
    app.get('/', (req, res, next) => {
        const number = req.query.number;
        factorialworker.postMessage(number);
        res.json({ message: "All good" });
    });
    factorialworker.on('message', (data) => {
        console.log("Here is your factorial", data);
    })
    httpsServer.listen(port, () => {
        logger.info(`App listening on port ${port}`);
    });
}
init();
Enter fullscreen mode Exit fullscreen mode

And you factorial.js will be

const { parentPort } = require('worker_threads')
const getFactorial = (number) => {
    if (number === 1) return 1
    return number * getFactorial(number - 1);
}
parentPort.on('message', (number) => {
    parentPort.postMessage(getFactorial(number))
});
Enter fullscreen mode Exit fullscreen mode

Git REpo for Code: https://github.com/shaikh-kamran/nodejs-worker-thread-example
Hope this blog helps you understand the implementation of worker thread in Node js application,Β 
Happy Koding everyone!!!

Top comments (0)

🀯

"I made 10x faster JSON.stringify() functions, even type safe"

☝️ Must read for JS devs