Intro to Node.js
- Intro
- JavaScript engine
- JavaScript runtime
- Threads
- Event loop
- Overall picture
- Blocking vs Non-blocking
- Reference:
Intro
According to Node.js website Node.js is a JavaScript runtime built on Chrome’s V8 JavaScript engine.
Ok, but what this means? Well, basically Node.js allows you to run JavaScript outside the web browser, which was the mainly place where JavaScript was used.
With Node.js you can run code on the server side and not only in clients (web browsers mainly).
JavaScript engine
Let’s focus on the definition but from the to the beginning:
built on Chrome’s V8 JavaScript engine
V8 is a Google open source javascript engine. It is written in c++, as node and chrome are.
There are several engines like Google’s V8 or Mozilla SpiderMonkey.
A JavaScript engine is a piece of software that translates JavaScript code into the platform machine code allowing to be executed by the CPU.
Node.js
^ |
| | Code
Result | | and
| | bindings
| v
V8
V8 needs bindings in order to return results that are not part of the language, for example, access to the filesystem.
What Node.js do is wrap v8 with custom options, we will see more details in the following sections.
In 2017 V8 introduces Node.js in its testing suite, officially making Node.js a target for the JS engine, in addition to Chrome.
JavaScript runtime
Continuing with the definition:
Node.js is a JavaScript runtime
Node.js or Chrome don’t use the engine directly. The JavaScript engine works inside a JavaScript runtime, which provides bindings and additional information to the scripts at runtime.
These can be utility libraries or APIs which allow communicating with the world surrounding the engine. An example here might be access to information about the web browser in which your script is executed, environmental information for Node.js or anything needed to the proper functioning of the software.
With that all, now we know that Node.js is not a programming language, the language is javascript. This is a common misconception.
Threads
Another misconception is think Node.js only uses one thread.
Node.js apps run in a single process, without creating new threads to handle the new incoming requests.
There is a worker threads pool inside the runtime. Those threads are controlled by the event loop.
In this context concurrency refers to the event loop’s capacity to execute JavaScript callback functions after completing other work. This is the reason to be careful and don’t block the event loop, or the worker threads.
Event loop
Node.js runs JavaScript code in the Event Loop and offers a Worker Pool to handle expensive tasks like file I/O.
When programming in node we should try to program in an event driven programming fashion, which is a programming paradigm where the execution is guided by events like incoming requests. This remains me the old days when I did some code in second life.
In Node.js the event loop allows performing non-blocking I/O operations using the system kernel whenever possible.
Most modern kernels are multi-threaded, so with one Node.js thread handles multiple operations executing in the background by using the system kernel.
Overall picture
Blocking vs Non-blocking
Node.js uses an event-driven, non-blocking I/O model.
I/O is the communication between node and the external world, like network (fetch some records from the database or to write them), hard drive (read or write files).
While Node.js is doing I/O, it is not blocked. This model is used because if the browser is fetching something like images, it does not block the page, offering a better experience for the user.
We can see the difference with two simple examples. All the code is here.
This code is blocking:
const fs = require('fs');
var timeStart = process.hrtime()
function readBook() {
const data = fs.readFileSync('./el_quijote.txt');
let convertedData = data.toString();
let numberOfLines = convertedData.split("\n");
console.log(numberOfLines.length - 1);
}
readBook();
console.log('Are we sure?');
readBook();
totalTime = process.hrtime(timeStart)
console.log(`Time: ${totalTime[0]}s ${totalTime[1]/ 1000000} ms`);
The result is:
yarn run v1.22.4
$ node index.js
2186
Are we sure?
2186
Time: 0s 15.637186 ms
Done in 0.12s.
This code is non-blocking
const fs = require('fs');
var timeStart = process.hrtime()
function readBook() {
fs.readFile('./el_quijote.txt', (err, data) => {
if (err) throw err;
let convertedData = data.toString();
let numberOfLines = convertedData.split("\n");
console.log(numberOfLines.length - 1);
});
}
readBook();
console.log('Are we sure?');
readBook();
totalTime = process.hrtime(timeStart)
console.log(`Time: ${totalTime[0]}s ${totalTime[1]/ 1000000} ms`);
The result is:
yarn run v1.22.4
$ node index.js
Are we sure?
Time: 0s 4.092307 ms
2186
2186
Done in 0.14s.
The following diagram should help to understand whats going on
Blocking Non Blocking
+----------+ Read first file +-+ Start to read first file
+----------+ +-+
+-+ Print first result +-+ Start to read second file
+-+ +-+
+-+ Print Question +-+ Print Question
+-+ +-+
+----------+ Read second file +----------+ Print first result
+----------+ +----------+
+-+ Print second result +----------+ Print second user
+-+ +----------+
In the blocking scenario what happens is:
- Read first file
When finished: - Print the result
When finished: - Print question
When finished: - Read second file
When finished: - Print the result
So until the operations do not finish, it does not continue with the others.
In the non-blocking scenario what happens is:
- Start reading first file
- Start reading second file
- Print question (Because it is not blocked and can continue)
When finished reading the file: - Print the first result
- Print the second result
By using non-blocking the thread and using properly the event loop, we can reduce the time from 15 ms to 5 milliseconds
Reference:
https://stackoverflow.com/questions/36766696/which-is-correct-node-js-architecture http://abdelraoof.com/blog/2015/10/28/understanding-nodejs-event-loop/ https://nodejs.org/en/docs/guides/