You may have heard that Node.js uses worker threads to perform operations such as reading a file. But did you know that not every operation goes to worker threads?
"Did you know that there are asynchronous non-blocking operations at the operating system level that Node.js uses directly without worker threads?"
System calls are traditionally blocking
System calls, such as reading a file, are traditionally blocking. First the call is initiated, then it waits for necessary operations at the operating system level to take place. When the operating system side is complete the results are passed on and the function will return.
There is possibly a large time in between initiating the call and actually getting the results. The time in between is spent waiting. The executing thread is halted and nothing else takes place.
Make use of waiting time
The guiding principle behind Node.js is to make use of the time spent waiting for an operation to complete. Other requests should be advanced while one request is waiting on something. In order to make this happen, a blocking operation has to be made non-blocking.
One way of making a blocking system call non-blocking is to perform it in a separate thread. This way the main thread is free to continue working on other things as the blocking operation is blocking a completely different thread. This is the solution Node.js uses for blocking system calls.
Non-blocking system calls
Not every system call is blocking. Asynchronous processing model is known to be performant. And this fact is know at the operating system level, too.
There are non-blocking system call implementations available for some operations. They aren't widespread and available for every possible operation, but there are some. And Node.js can make use of these.
What goes to worker threads
The current state of non-blocking system calls varies greatly across platforms. Node.js is faced with the difficult task of trying to provide unified operation across all of these.
For some operations Node.js can leverage non-blocking operations uniformly across platforms. This is the case for network I/O. For some a worker thread approach is still used. This is the case for filesystem operations.
The following table describes in more detail which operation goes to worker threads and which don't.
Table 1. Operations that are performed in main thread using asynchronous non-blocking system calls or in worker threads.
Database drivers and other 3rd party libraries
In the end
Node.js uses worker threads to perform blocking operations. This way, the time spent waiting for I/O to complete can be used to advance other requests. However, there is growing support for asynchronous operations at the operating system level. It varies greatly across platforms but for some operations such as network I/O it is there. For such operations, Node.js can leverage non-blocking system calls directly without using worker threads.
Node.js doesn't wait for your database call to finish?
Learn the right mental model for Node apps. Get free email course and two chapters from Finish Your Node App book.