I've been doing a lot of research into the core of Node.js lately, and I have some questions about the inner workings of the Node platform. As I understand it, Node.js works like this:
Node has an API, written in Javascript, that allows the programmer to interact with things like the filesystem and network. However, all of that functionality is actually done by C/C++ code, also a part of Node. Here is where things get a little fuzzy. So it's the Chrome V8 engine's job to essentially "compile"(interpret?) javascript down into machine code. V8 is written in C++, and the Javascript language itself is specified by ECMA, so things such as keywords and features of the language are defined by them. This leads me to my first few questions:
How is the Node Standard Library able to interact with the Node Bindings, since the Node Bindings are written in C++?
How does the Chrome V8 engine interpret Javascript in the context of Node? I know it uses a technique called JIT, which was mentioned in a similar question: (https://softwareengineering.stackexchange.com/questions/291230/how-does-chrome-v8-work-and-why-was-javascript-not-jit-compiled-in-the-first-pl) But this doesn't explain how Javascript is interpreted in the context of Node. Is the Chrome V8 engine that ships with Node the exact same engine that runs on the Chrome browser, or has it been modified to work with Node?
That brings me to my next question. So Node features event-driven, non-blocking IO. It accomplishes this via the Event Loop, which, although it is often referred to as the "Node Event Loop", is actually a part of the libuv library, a C++ library designed to provide asynchronous IO. At a high level, the event loop is essentially accessed via Callbacks, which is a native Javascript feature and one of the reasons Javascript was chosen as the language for the Node project. Below is an illustration of the how the event loop works:
This can also be demonstrated live by this neat little site: http://latentflip.com/loupe/ Let's say our Node application needs to make a call to an external API. So we write this:
request(..., function eyeOfTheTiger() {
console.log("Rising up to the challenge of our rival");
});
Our call to request
gets pushed onto the call stack, and our callback is passed somewhere, where it is kept until the request operation finishes. When it does, the callback is passed onto the callback queue. Every time the call stack is cleared, the event loop pushes the item at the top of the callback queue onto the call stack, where it is executed. This event loop is run on a single thread. Where problems arise is when someone writes 'blocking' code, or code that never leaves the call stack and effectively ties up the thread. If there is always code executing on the call stack, than the event loop will never push items from the callback queue onto the call stack and they will never get executed, essentially freezing the application. This leads me to my next question:
I've found this image as a demonstration of the process:
This is where I'm uncertain about how exactly Chrome V8 engine and libuv interact. I'm inclined to believe that the Node Bindings facilitate this interaction, but I'm not quite sure how. In the image above, it appears as though the NodeJS bindings are only interacting with machine code that has been compiled down from Javascript by V8. If so, than I am confused as to how the V8 engine interprets the Javascript in such a way that the Node Bindings can differentiate between the callback and the actual code to immediately execute.
I know this is a very deep and complicated series of questions, but I believe this will help to clear up a lot of confusion for people trying to understand Node.js, and also help programmers to understand the advantages and disadvantages of event-driven, non-blocking IO at a more fundamental level.
Status Update: Just watched a fantastic talk from a Sencha conference(Link here). So in this talk, the presenter mentions the V8 embed guide(Link here), and talks about how C++ functions can be exposed to Javascript and vice versa. Essentially how it works is that C++ functions can be exposed to V8 and also specify how it wants those objects to be exposed to Javascript, and the V8 interpreter will be able to recognize your embedded C++ functions and execute them if it finds Javascript that matches what you specified. For example, you can expose variables and functions to V8 that are actually written in C++. This is essentially what Node.js does; it is able to add functions like require
into Javascript that actually execute C++ code when they are called. This clears up question number 1 a little, but it doesn't exactly show how the Node standard library works in conjunction with V8. It is still also unclear about how libuv is interacting with any of this.
With asynchronous programming, we can execute other code while we wait for long activities like network requests to finish. JavaScript code is executed on a single thread within a computer process. Its code is processed synchronously on this thread, with only one instruction run at a time.
Node. js uses callbacks, being an asynchronous platform, it does not wait around like database query, file I/O to complete. The callback function is called at the completion of a given task; this prevents any blocking, and allows other code to be run in the meantime.
Asynchronous programming is a technique that enables your program to start a potentially long-running task and still be able to be responsive to other events while that task runs, rather than having to wait until that task has finished. Once that task has finished, your program is presented with the result.
Asynchronous programming makes it possible to express waiting for long-running actions without freezing the program during these actions. JavaScript environments typically implement this style of programming using callbacks, functions that are called when the actions complete.
Asynchronous programming in Node.js JavaScript is asynchronous in nature and so is Node. Asynchronous programming is a design pattern which ensures the non-blocking code execution. Non blocking code do not prevent the execution of piece of code.
The asynchronous code will be written in three ways: callbacks, promises, and with the async / await keywords. Note: As of this writing, asynchronous programming is no longer done using only callbacks, but learning this obsolete method can provide great context as to why the JavaScript community now uses promises.
Synchronous JavaScript: As the name suggests synchronous means to be in a sequence, i.e. every statement of the code gets executed one by one. So, basically a statement has to wait for the earlier statement to get executed. Let us understand this with the help of an example.
Node.js is a popular open-source runtime environment that can execute JavaScript outside of the browser. The Node runtime is commonly used for back-end web development, leveraging its asynchronous capabilities to create networking applications and web servers. Node also is a popular choice for building command line tools.
Basically what you are looking for is V8 Templates. It exposes all your C++ code as JavaScript functions that you can call from within the V8 Virtual Machine. You can associate C++ callbacks when functions are invoked or when specific object properties are accessed (Read Accessors and Interceptors).
I found a very good article which explains all of this - How does NodeJS work?. It also explains how libuv works in conjunction with Node to achieve asynchronicity.
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With