Before jumping right into why nodejs is considered efficient compared to other backend technologies, we must first understand the difference between thread-based and event-driven systems. Nodejs is event-driven whereas Python, Ruby, PHP is thread-based and this is one of the major reasons why nodejs is more efficient compared to Python Ruby and PHP. So does this mean we should always use nodejs and scrap other traditional technologies? Certainly no, we will discuss this in detail in this article.
Let’s brush up some basic concepts first
Multithreading vs multiprocessing?
Multiprocessing is nothing but utilizing all the CPU cores available for computation. So Let’s say your laptop has 4 CPUs, you can have a maximum of 4 processors executing different tasks concurrently whereas in case of multithreading you have just one core in which you spawn multiple threads. So no matter how many threads you open, internally the CPU is just switching between the task in different threads but executing only one of them at any given point in time.
What is thread-based and event-driven systems?
Multithreading is used to divide up the work. In web apps, every request starts a new thread, but there were many overheads with threads such as thread safety, locking strategies for concurrent threads. Hence even Python Ruby and PHP were single threaded by default with the option of using multiple threads wherever required. Nodejs however is single-threaded and even if you want, you cannot have multithreading. Nodejs has an event loop which continuously polls asynchronous tasks and executes the callback when the task is completed. This topic will be discussed in detail later in this article
So how is nodejs efficient with single thread than other technologies which can be multithreaded?.
So far we have just discussed the problem in detail now let’s dive into the real reason of why nodejs is faster. Backends generally don’t perform heavy computations. The program most of the time is waiting for either a DB response or waiting for the disk to read and write. So the server is idle most of the time. For example, if the DB query takes about 10ms, at a clock speed of 2GHz the CPU could have performed 2 X 10^9 instructions. This time can be very well saved in python as well by using multithreading but that brings an overhead of software complexity. For example reading a file is a blocking operation, you can fire up a new thread which reads the content of a file while you continue executing other instructions and the main thread is notified as soon as the content of the file is read. This is nothing but thread based systems which is kind of tedious when there are so many operations to handle simultaneously and is definitely not a scalable solutionNodejs solves this problem very smartly using a single event loop with just one main thread and a background kernel thread. Nodejs maintains a STACK of all the instructions it needs to execute which are non blocking.
What is a Callstack ?
We know what a stack data structure is.. LIFO. It’s the same here. Whenever a function is called, all the instructions in the function are pushed on top of the stack one by one. If there is a function inside a function, nodejs get’s all the instructions inside the function and pushes into the stack. Confused? Let’s take an example to understand this in detail.
main() is the first function that is called so the reference of the function is pushed into the call stack you can on the right
main() calls another function getHypotenuse with two parameters – 3, 4. Since the main functions execution is not yet complete, it remains in the call stack and a reference to getHypotenuse is also pushed into the stack.
getHypotenuse calls another function square with parameter – 3. So reference to function square is pushed into the stack
Square function returns the square of 3 and is stored in the local variable aSquare of getHypotenuse() and square function is popped out of the stack since it has completed execution of all it’s instructions.
Again square is called with parameter 4, square is pushed into the stack.
bSquare is calculated and stored in the local variable in getHypotenuse.
sumOfSquares is evaluated and again stored in the local variable of getHypotenuse()
Math.sqrt is another inbuilt function which is then pushed into the stack
Sqrt is evaluated and stored at a reference and the reference to the variable is returned by getHypotenuse()
Now the execution of all instructions inside getHypotenuse is also complete, hence it is popped out of the stack. The returned value is stores in the local variable hypotenuse in main(). The next instruction console.log is pushed into the stack
Finally main() completes execution without returning any value, hence it is popped out of the stack.
This is how nodejs uses just one thread to execute all the instructions concurrently.
Is Nodejs completely single threaded ?
No not really. Nodejs has one main thread but some standard libraries like the fs module runs outside of the nodejs single thread in the background to improve performance and efficiency.
So why do we call Nodejs single threaded?
Any task in nodejs is classified into two types
Micro tasks – any non blocking operation is considered as micro task such as console.log, mathematical operations, object manipulations.
Macro tasks – any blocking tasks such as I/O operation, setTimeout, setInterval, api calls is considered Macro tasks.
Some operations like I/O is sent to the background kernel thread since most of the time this thread is idle and waits for the response. This helps the main thread to continue execution. As soon as the background thread is ready with all the data required for operation, it returns it to the main thread for execution. All the async operations in nodejs are handled by libuv (a library module) thread pool which is composed of four threads to offload heavy operations from the main thread.Note: background thread doesn’t execute any operation. It just helps the main thread to go ahead executing while it does all the waiting.
What is Nodejs event loop ?
Nodejs has just one main thread and one call stack. So whenever there is a request with a callback. The callback needs to wait until the call stack is empty again. All the callbacks wait in a queue called as task queues or message queues. As soon as the call stack is empty, the main thread checks for all the callbacks that needs to be executed and this continues in an infinite loop. Hence it is known as event loop.
So is event loop the reason why set timeout of 0 sometimes magically fixes things in the browser?
Yes. Now that you have understood the event loop, it should make sense why sometimes setTimeout fixes things magically. When you call a function inside setTimeout of 0, the function is added in the task queue and waits until the call stack is empty and is pushed into the call stack as soon as no more instruction is left in the call stack. Since many operations are executing parallelly in the browser, sometimes just changing the execution sequence can solve bugs.
When to use nodejs and when to use thread based systems like python?
We have understood how nodejs is efficient compared to python ruby or PHP, but does that mean we should always use nodejs? No, When it comes to heavy computation, Nodejs is not the best choice. Nodejs was created to solve I/O scaling problems. If the operations are CPU intensive than it’s better to use python, java, golang etc. because you have the liberty to use all the available CPU cores which is not the case with nodejs.
331 total views, 1 views today