How JavaScript/Node Works Behind the Scene
A “high-level single-threaded, garbage-collected, interpreted (or just-in-time compiled), prototype-based, multi-paradigm, dynamic language with a non-blocking event loop” language
JavaScript is an interpreted language, it needs an interpreter in the environment to read the source code and execute it, whereas languages like Java and C are compiled languages, that statically analyze all your code in advance, and compile it down to binary which you can run in the machine.
How do you run JavaScript?
You need a JavaScript engine that implements JS runtime
JavaScript Engine
Takes code as input and undergoes 3 major phases: PARSING, COMPILATION, and EXECUTION.
- Parsing — code is broken down into an array of tokens and converted into AST (Abstract Syntax Tree).
- Compilation — decides whether JS is interpreted or compiled language, can use interpreter along with compiler to make it JIT (Just in time) compiled language.
- Execution — The interpreter and compiler are in sync with the execution phase making use of the Memory heap and Call stack.
What is V8?
Internal JavaScript engine built in Chrome, which compiles all the JS codes to native machine code before running it, as composed to interpret bytecode line by line like a normal interpreter.
Compilation
- JIT compilation: Just-in-time compilation, which JavaScript uses, generates machine code during runtime
- AOT compilation: Ahead-of-time compilation, in which the compiler takes a piece of code (which is supposed to be executed later) and tries to optimize it.
Is Web Browser a JavaScript runtime?
A browser contains a Javascript engine (for example Chrome v8). The engine implements a Javascript runtime, which includes the call stack, heap, and event loop. The browser also usually includes a set of APIs that augment the Javascript runtime and make asynchronous code execution possible. NodeJS also implements a Javascript runtime using Chrome’s v8 engine as well as the Libuv library (event loop and worker threads).
JavaScript Mechanism (how to handle the sync and async code)
The event loops behind the browser handle the sync and async JavaScript code, like when the JS engine that is built in the browser (for Chrome is V8) runs JS code because JS is a single-threaded language, the code will be read line by line, and stores the memory in the heap, and push the function call to the call stack. If it is an async function code, it will be then pushed to the web API instead to wait for the condition to be met while the call stack keeps running as first in last out, and garbage collects the variables that are no longer in use. Once the async code in the web API is ready to run, it will then be pushed to the Message Queue. When there are no functions to run in the call stack, the Event Loop will take the first event from the Queue and push it to the Call Stack to run.
Event loop (better to watch Youtube Video to understand)
- In JS there are 3 types of memory:
stack
used for the function calls,heap
for each of the objects,queue
— setTimeout. - JS engine executes the function stack first. If the stack is empty, it pops the event from the queue. If the event queue has another function call, it pushes it to stack and executes it again until it is empty. This is called the event loop
Garbage Collection
- JavaScript has automatic memory management, it automatically performs garbage collection
- garbage collection automatically cleans things up in your memory
- garbage means class objects which are instantiated but not in use anymore, they occupy memory, and the compiler should remove them
GC Process
- obj, var, func allocation -> obj, var, func read/write location -> process is continued until the program ends -> memory is released when the program ends
- as long as references exist, there will not be any cleaning or garbage collecting
- if a location is unreachable then that will be collected/released the memory as garbage
Stack vs Heap
- When the Heap becomes full, garbage is collected.
- Stack is last in first out, so there is no need to garbage collect.
- => Stack memory is collected automatically when the execution path reaches the end of the scope
Web API
- All the code execution is done inside the call stack, which is present in the JS engine, which in turn is in the browser.
- The browser has some added functionalities like LocalStorage, Timer, SetTimeout(), Fetch(), Console, Location, Address field, DOM APIs, etc.
Asynchronous Web API
- Q. Are only asynchronous web API callbacks, registered in the web API environment?
- A. YES, the synchronous callback functions like what we pass inside map, filter, and reduce aren’t registered in the Web API environment. It’s just those async callback functions that are registered.
Server-side Responsibility
- client => generating web pages
- server => providing a gateway to the data, develop endpoints that the client can talk to get or save various pieces of data
- The server provides an API to clients, each API is like a button on a remote control, and all APIs we set up represent the interface of how we interact with the client.
Node.js
- Node is a JavaScript runtime environment (not programming language or a framework) built on top of libnv — a cross-platform asynchronous I/O based on event loop
- One of Node's core strengths: is faster processing!
- wait for events -> accept event -> dispatch event -> handlers are called
- Node is non-blocking => Rather than waiting for an operation to finish, create a callback that will be invoked when the operation ends
- Node.js server runs a single-threaded event loop that creates async threads which is the nature of the non-blocking IO
- Node => event-driven non-blocking asynchronous code; the main event loop is single-threaded but most of the IO runs on separate threads
why node?
- asynchronous I/O, event-driven architecture, light-weightedness, speed, scalability, use popular language JavaScript, NPM library
- most popular for its asynchronous event-driven, non-blocking I/O processing.
- it gets most of this concurrency and asynchronism from Javascript’s single-threaded event loop model.
global in node
- like
window
in the browser, but we useglobal
in node repl, which has a bunch of global objects that we can use global.process.argv
- return an array containing the command line arguments passed when the node process was launchedprocess.argv[2]
- when we passnode hello.js learn
- here we get to learnglobal.console.log("Hello")
Blocking vs Non-blocking
- blocking: read code line by line, read file 1 then read file 2 (we can write multi-threaded code through the library to get faster processing)
- e.g. Java, choose a driver manager connect to a driver, get a connection, open that connection, prepare a statement, execute, close the connection => call blocking
- non-blocking: asynchronously event-driven model, you read in both files at the same time => processing code, handling things much faster
- non-blocking is good for microservices architecture, or standard web applications with a node backend
High-Level Threads
- Node under the hood is written in C++, the event loop is implemented via libuv (using C++).
- Threads run in processes, one process can have many threads in it, and as they are in the same process, they share a memory space
- Events are pushed to the main thread, then worker threads process the request
Node Modules
- Event Module — emit (call) / on (listen) / removeListener (unsubscribe) — like addEventListener
- FS — file system module, both sync and async, the data returned as a Buffer object, toString() converts the buffer to a string.
- Path — path module, handling and transforming file paths
- HTTP — for creating HTTP-based web applications, createServer (like building endpoints using express)
- Net — for creating TCP-based servers and clients (chat app) — slower but less likely to lose data packets
- dgram — for creating UDP/Datagram sockets (DatagramSockets) — faster, simpler, like for videos audios, you do not care to lose data
- HTTPS — for creating TLS/SSL clients and servers
import & export
//CommonJS - loads modules synchronously
const express = require('express');
module.exports = Task;
//Modern ES Modules (ESM) - loads modules asynchronous
import process from "process";
export default App;
Basic ExpressJS Setup
const NewModel = require("../models/example");
const express = require("express");
const app = express();
const port = process.env.PORT || 3000;
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.use(express.static(__dirname + '/public')); // static resources
app.get("/", (req, res) => {
let currentData = await NewModel.find({});
res.render("homeView", { data: currentData });
});
app.use((req, res) => {
res.status(404);
res.render("404");
});
app.listen(port, () => { console.info(`Application Started. Port: ${port}`); });
//const mongoose = require('mongoose');
//mongoose.connect('url').then(() => {app.listen(3000);}).catch(err => {console.log(err);});