There are some common misconceptions around node and its working.
Node is single threaded.
Node is Multi threaded.
Event loop is single threaded, but other io's like file io, network io etc are Multi threaded.
Node runs in one thread, v8/javascript runs in another thread.
There are only 3 major components for node:
V8 => To execute javascript(JS engine).
Event loop => Managed by external library called libuv.
Internal Modules=> crypto, tcp, net, http etc.
Node currently uses V8, google's JS engine to run javascript related stuffs, on top of this it also uses libuv eventloop to manage asynchronous IO, both JS engine and libuv eventloop runs on same single thread which we call it as main thread.
Now the real question is how node uses this single thread to manage asynchronous IO(file read write, network io etc).
Node uses both main thread as well as spawn system level threads using libuv based on the tasks.
Basically Node is both single thread as well as multithread, based on the tasks that it is currently doing.
For network io's(fetch etc) node uses the same main thread, for other tasks like filesystem io,
crypto module to perform some encyrptions etc, DNS lookups etc it uses system level threads.
NOTE: worker-threads is not related to any of the above stuffs, we are discussing currently.
Consider the below example on how node utlizes multiple threads to perform File-io.
import fs from 'fs/promises';
const main = async () => {
console.log(`current threads: ${process.env.UV_THREADPOOL_SIZE}`);
const startTime = Date.now();
const promise = new Array(50).fill(0).map(_ => fs.readFile('./test.txt'));
await Promise.all(promise);
console.log('Finished running task in', (Date.now - startTime) / 1000, ' seconds');
}
(async () => {
&nbssp; await main();
})()
Output
The above code basically reads a file of size 750MB 50times concurrently. From the output you
can clearly see that when specifying the number of threads to 1 the entire program took 12S
whereas if we specify the number of threads to 50, the running time reduced to 4S 4times less than
the previous one.
From this we can clearly confirm that node indeed uses threads for File level IO.
Consider the below example for the same but in this case its a network request.
import fs from 'fs/promises';
const main = async () => {
console.log(`current threads: ${process.env.UV_THREADPOOL_SIZE}`);
const startTime = Date.now();
const promise = new Array(50).fill(0).map(_ => fetch('https://example.com'));
const res = await Promise.all(promise);
console.log(res.map(r => r.status));
console.log('Finished running task in', (Date.now - startTime) / 1000, ' seconds');
}
(async () => {
&nbssp; await main();
})()
Output
From the above output we can clearly confirm that for network requests node uses the same main thread. checkout this blog to know more on how efficiently node uses the same main thread to handle n number of network io's.
This is what i want to cover in this blog,on explaining how node efficiently uses threads to handle asynchronicity. I hope there are some takeaway from this and can help atleast one to understand node better.