How Google's V8 Engine Powers JavaScript Execution
Let's Start from the Beginning
Each Browser Like Chrome, Firefox, Safari, Microsoft Edge, . . . has its own built-in Javascript Engine which runs the Code and some Web APIs that help developers to build on the web. The Basic Outline of the Javascript Run-time Environment in the Browser Looks like this
Javascript Engine
The Javascript Engine is different from the Javascript Run-Time Environment. The Whole Picture above is a JavaScript Run-Time Environment and the Layout with JavaScript Symbol is a Javascript Engine
The Javascript Engine is of two Parts
Call Stack: The call stack is similar to the Stack data structure it keeps track of the execution context of your JavaScript code. It manages the order in which functions are called and tracks the variables and parameters of those functions.
Heap: The heap is a region of memory used for dynamic memory allocation. It is where objects and data structures with longer lifetimes are stored. In JavaScript, this includes objects, arrays, and closures.
The Working flow of the Javascript Engine is as shown below
Parser ( Parsing ): In this step, the Code is broken into tokens, and the Syntax Parser Convert the code into Abstract Syntax Tree
Let's Consider a Basic greet Function Program
function greet() {
console.log("Hello World");
};
greet();
The Abstract Syntax Tree of the above JavaScript Code looks like
{
"type": "Program",
"body": [
{
"type": "FunctionDeclaration",
"id": {
"type": "Identifier",
"name": "greet"
},
"params": [],
"body": {
"type": "BlockStatement",
"body": [
{
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "MemberExpression",
"object": {
"type": "Identifier",
"name": "console"
},
"property": {
"type": "Identifier",
"name": "log"
},
"computed": false
},
"arguments": [
{
"type": "Literal",
"value": "Hello World",
"raw": "\"Hello World\""
}
]
}
}
]
}
},
{
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "Identifier",
"name": "greet"
},
"arguments": []
}
}
],
"sourceType": "module"
}
You can change the code and see the Abstract syntax of different codes at https://astexplorer.net/
Ignition ( Interpreter ): The V8 interpreter, Ignition, parses and executes JavaScript source code by converting it into bytecode for efficient, quick execution without the need for full compilation.
Turbofan ( Compiler ): TurboFan, V8's optimizing compiler, translates JavaScript bytecode into highly optimized machine code for faster execution and improved performance.
Orinoco ( Garbage Collector ): The Garbage Collector Clears the Memory Space when it is possible and this V8 Garbage Collector uses a Mark and Sweep algorithm to free up space
Mark Phase: All objects are marked as 0 initially (at creation) and in the mark phase the objects that will be accessible are marked as 1 (reachable) by a DFS graph traversal.
Sweep Phase: During the sweep phase, the objects marked with 0 are removed from heap memory. and also all reachable objects are again initialized with 0 (made unreachable) because the algorithm will run again.
JavaScript Run-Time Engine
Let's see the Overview of Google's V8 Run-Time Engine
The Javascript waits for None it just Starts Executing whatever is present in the call stack and the Event loop is responsible for Inserting the Elements into the Call Stack
Here we have two Queues in which the Tasks are Inserted from one end and Taken and pushed into the Call stack from the Other end
Let's Explore the JavaScript Runtime Environment Through a Practical Code Example
console.log("Start!");
setTimeout(() => {
console.log("Timeout!");
}, 0);
Promise.resolve("Promise!")
.then(res => console.log(res));
console.log("End!");
The above Code contains a setTimeout( ) method and a Promise to be resolved these two functions are Asynchronous
First, the JavaScript Engine Encounters the Synchronous function and also web API console.log "Start" and prints it in the console
Further, It encounters setTimeout( ) method as it's Asynchronous JavaScript Engine will push it into the Macrotask Queue and Continue the Further Execution
Later on, the JavaScript engine Encounters Promise which is to be resolved as it is Asynchronous is pushed into the MicroTask Queue
As the JavaScript Waits for none it Continues the Execution and Print "End!" in the Console
As the Call Stack is Empty the Event Loop Pushes the Resolved Promise present in the Microtask Queue into the Call Stack and pops the task and Execute the Resolved Promise Printing "Promise!" onto the Console
The call stack and Microtask Queue are Empty now so the Event loop looks into the Macrotask Queue for any tasks that exist and finds the setTimeout( ) method with Time Completed it pushes the function in the call stack and the call stack pops and executes the Code block Printing "Timeout !" in the Console
In conclusion, delving into the inner workings of Google's V8 engine has unveiled the intricate dance of components and optimizations that power the JavaScript we write every day. From parsing and bytecode generation to the execution of highly optimized machine code, V8 stands as a testament to the continuous efforts in enhancing JavaScript performance.
As web development continues to evolve, so too will the capabilities and optimizations of V8. Understanding how V8 functions under the hood empowers developers to write more efficient code and make the most of this powerful engine.
Putting everything together, now we can see a completed version of how Chrome V8 works from a Moderate-level view.
Thank you for taking the time to read this article. For more articles on similar topics, please feel free to explore my profile. If you have any questions or inquiries, don't hesitate to reach out to me through my social media channels. Your engagement and feedback are greatly appreciated.
Subscribe to my newsletter
Read articles from Md. Sadiq directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Md. Sadiq
Md. Sadiq
Passionate Problem Solver