Here is a series of articles to help you create backend applications in Javascript.
Node.js is now a must, so it is essential for a developer to master it.
I will publish a new article every two days and little by little you will learn everything there is to know about Node.js
To not miss anything follow me on twitter: https://twitter.com/EricTheCoder_
How NodeJS work under the hood?
In this section we will do a little theory and find out how NodejS executes its JavaScript code.
As you know, NodeJS allows to execute asynchronous code. This concept may seem simple but in the background it is a bit more complicated. What determines what code is executed? What determines the order of execution?
Understanding these concepts is essential for developing with NodeJS. No need to become an expert on the subject but at least understand the basics.
Note that some concepts have been simplified in order to better explain them.
The architecture of NodeJS
NodeJS is composed of two main parts the V8 machine and the libuv library
The V8 machine
Take care of converting JavaScript code into machine code. Once the code has been converted into machine code, the execution will be managed by the libuv library
libuv
Is an open-source library, written in C ++ which specializes in asynchronous i/o execution (ex. File system, Networking and more)
libuv implements two very important features of NodeJS: the Event Loop and the Thread Pool
An important point to understand is that NodeJS works in single thread mode.
That is, it can only perform one task at a time. If a task requires too much time/resource then it will block/prevent other tasks from running.
Imagine, for example, if there were 100,000 users on the site at the same time requesting access to the database, the response time would quickly become unacceptable. This is why NodeJS needs efficient management of asynchronous code execution .... That's the job of the Event Loop
The Event Loop is used to manage asynchronous code such as callbacks, network promises and requests which require little resource. And when a task takes too long to execute, so as not to block the thread, the Event Loop will delegate this work to the Thread Pool.
The Thread Pool can run tasks in parallel and therefore takes care of more cumbersome tasks such as access to the file system and very demanding processes such as for example video conversions or cryptography.
Execution order of a NodeJS application
When running a NodeJS application, the initialization code, 'requires' and top level code are executed immediately one after the other.
The callbacks encountered in our code are not executed immediately because potentially blocking, it would block the application to other tasks and other users. These callbacks are therefore registered with the Event Loop
Once the "top level" code has been executed, NodeJS will give control to the Event Loop so that it can execute the tasks it contains.
The Event Loop decides, according to pre-defined criteria, which execution order must be respected. The Event Loop can also decide to delegate a really long task to the Thread Pool. (eg access to the file system).
The Thread Pool can execute several tasks at the same time (multi-thread) and will return the result to the Event Loop
As long as there are tasks to be performed, the Event Loop will keep the application active.
Once all the tasks of the Event Loop are finished, control is given back to the main Thread of your application which will terminate the program.
NodeJS as an example
The theory is fine, but let's review it all this time with a concrete example
const fs = require('fs')
console.log('First task started')
fs.readFile('./data/products.json', 'utf8', (err, data) => {
console.log(data)
console.log('First task ended')
})
console.log('Second task started')
Résultat
First task started
Second task started
{
"name": "iPhone 12",
"price": 900
}
First task ended
Based on the logic explained earlier, NodeJS will execute the code in the following order:
→ const fs = require (fs)
→ console.log('First task started')
→ registering the readFile callback with the Event Loop
→ console.log('Second task started')
→ High level tasks completed, the hand is therefore passed to the Event Loop
→ readFile callback → Delegate to the Thread Pool
→ When the readFile is finished
→ console.log(data)
→ console.log('First task ended')
→ If no other pending task then ends the Event Loop
→ End of the program
Exemple with SetTimeout Zero
console.log('First')
setTimeout(() => {
console.log('Second')
}, 0)
console.log('Thrid')
Result
First
Third
Second
You would have thought that with a setTimeOut of 0 it would be executed immediately? But no, as seen previously, NodeJS sends the callbacks to the Event Loop and executes the top level code first.
Based on this logic, the NodeJS will execute the code in the following order:
→ console.log('First')
→ register setTimeout callback with the Event Loop
→ console.log('Third')
→ Hand over to the Event Loop
→ callback setTimeout
→ console.log('Second')
→ If no other task then ends the Event Loop
→ End of the program
Server example
const http = require('http')
const server = http.createServer((req, res) => {
if (req.url === '/') {
res.end('<h1>Home page</h1>')
} else if (req.url === '/about') {
res.end('<h1>About page</h1>')
let i = 0
do {
i++
} while (i < 10000000000)
} else {
res.end('page not found')
}
})
server.listen(5000, 'localhost', () => {
console.log('Server is listening at localhost on port 5000')
})
There are two lessons to be learned from this example. First, the NodeJS app is never going to stop. The Event Loop is endless since it waits for events from the server. The 'listen' function keeps the Event Loop active.
Finally, when a user visits the about page, Node will execute the 'do while' and since it is not asynchronous code access to the website will be temporarily blocked for all users until the do while end. This is a good example of how NodeJS is single threaded and you have to be careful how you code your application.
For example, in this case, it would be better to place the do while inside an async function so as not to block the thread.
Conclusion
That's all for today, follow me on twitter: https://twitter.com/EricTheCoder_ to be notified of the publication of the next article (within two days).
Top comments (5)
What a great article, Eric!
I have a question, let's say I have 2 APIs and both are Asynchronous.
If 2 user call each api, almost at the same time, will the first API call block the second call?
It's a bit complex:
NodeJS is single thread so can only execute one line of code at the same time. But the execution is so fast that it is not a problem at all. Everything will run very very fast.
But, NodeJS can also do multi-thread for exemple if your API do a query to a database and return data, NodeJS will send that long process to a multi-thread execution to avoid blocking.
So NodeJS send only long process to multi-thread, everything else is executed in the single thread.
That's why NodeJS is a good match for web app that do database query but not that good match for computer intensive task like video manipulation or cryptography.
That means if both APIs have database reading/writing, those two APIs call probably won't block each other, is that right?
Exact
Thanks for the answer Eric, it's very clear