JavaScript: Understanding Under The Hood Of a Browser
Overview
This article is about deepening our knowledge of JS by understanding how our code gets executed. If you are new to JavaScript š¶, then this article will help to learn how JavaScript works. If you are an experienced š“, JavaScript developer hopefully, this will be a good refresher for you.
Understanding the browser environment, what it is composed of and how it works will make you more confident in writing JavaScript and well-prepared for potential issues that might happen.
The JavaScript Engine
Basically, the engine is a program that translates your JavaScript into machine code and executes the result on a computerās central processing unit (CPU). A popular example of a JavaScript engine is Googleās V8 engine.The V8 engine is used inside Google Chrome, Node.js ,electron and among others.And there are various JavaScript Engines that are available for use. You can refer this Wikipedia page for the list of available JS engines.
V8 Does:
- Compiles and executes JS code
- Handling call stack ā running your JS functions in some order
- Managing memory allocation for objects ā the memory heap
- Garbage collection ā of objects which are no longer in use
- Provide all the data types, operators, objects and functions
V8 Can:
- Provide the event loop, but this is sometimes implemented by the browser as well.
V8 Doesnāt:
Know anything about the Web APIs like DOM elements and events, XMLHttpRequest
, fetch
, setTimeout
, setInterval
and so on, which are accessible via JavaScript.
This is a very simplified view of what it looks like:
The mechanism consists of two main components:
- Memory Heap ā is an unstructured memory that is used for memory allocation of the variables and the objects.
- Call Stack ā is a LIFO data structure that is used for function calls that record where we are in the program.
š„ Call Stack
JavaScript is a single-threaded programming language with a single call stack. It means that your code is executed synchronously, we only have a single call stack, in other words, we can only execute one thing at a time.
If we execute a function, V8 pushes frame on top of the stack. When we return from a function, V8 pops off the frame. Letās take an example.
Following is the sequence of steps when JavaScript executes the code:
- Start execution using the main thread referred to as main() here.
- Identify the function which needs to executed and add it in the call stack.
- Identify other function which the current function executes and add it in the call stack.
- Upon completion of the function remove its reference from the call stack.
- Repeat steps #2 to #4 till the code finishes execution.
- After all the code has finished execution, remove main() from the call stack.
Hence, if any other program want to execute something, it has to wait until previous program is completely executed. This means that while a function is being executed, the engine cannot run another context at the same time. It also means that every time a function is pushed onto the call-stack, it then becomes the active executing context and takes control flow away from whatever function called it, until it returns either explicitly (with a return
statement) or implicitly (when all instructions have been executed).
So letās think of one scenario. What if a browser sends a HTTP request to load some data over network or to load an image to display on web page. Will browser freeze until that request is resolved? So how then do we achieve asynchronous JavaScript with itās illusion of multiple things happening all at once?
Browser comes with a JavaScript engine which provides JavaScript runtime environment and uses more than just a JavaScript engine. This is what browser under the hood looks like.
JavaScript runtime actually consist of 2 more components event loop and callback queue. Callback queue is also called as message queue or task queue.
Apart from JavaScript engine, browser contains different applications which can do variety of things like send HTTP requests, listen to DOM events, delay execution using setTimeout
or setInterval
, caching, database storage and much more. These features of browser help us create rich web applications.
But think about this, if browser had to use same JavaScript thread for execution of these feature, then user experience would have been horrible. Because even when user is just scrolling the web page, there are many things going on, in the background. Hence, browser uses low level language like C++
to perform these operations and provide clean JavaScript API to work with. These APIs are known as Web APIs.
These Web APIs are asynchronous. That means, you can instruct these APIs to do something in background and return data once done, meanwhile we can continue further execution of JavaScript code. While instructing these APIs to do something in background, we have to provide a callback function. Responsibility of callback function is to execute some JavaScript once Web API is done with itās work. Letās understand how all pieces work together.
So when you call a function, it gets pushed to the stack. If that function contains Web API call, JavaScript will delegate control of it to the Web API with a callback function and move to the next lines until function returns something. Once function hits return statement, that function is popped from the stack and move to the next stack entry. Meanwhile, Web API is doing itās job in the background and remembers what callback function is associated with that job. Once job is done, Web API binds result of that job to callback function and publishes a message to callback queue with that callback. The only job of event loop is to look at callback queue and once there is something pending in callback queue, push that callback to the stack. Event loop pushes one callback function at a time, to the stack, once the stack is empty. Later, stack will execute callback function.
Letās see how everything works step by step using setTimeout
Web API. setTimeout
Web API is mainly used to execute something after few seconds. This execution happens once all code in the program is done executing (when stack is empty).
What I think should be noted is the way setTimeout actually works. Calling setTimeout(function cb(){...}, 5000)
doesn't mean foo will be executed in precisely 5 seconds; it means that in 5 seconds cb
will be put into the callback queue, where it will wait until the stack is empty. This is why sometimes setTimeout(..., 0)
is used: instead of just executing its callback immediately, it waits until all the other stuff is done.
Thereās a resource called Loupe (also created by Philip Roberts) that visualizes JavaScript code and shows how the stack, the WebAPIs, the callback queue and the event loop interact with each other.
Heap
Sometimes V8 doesnāt know at compile time how much memory an object variable will need. All the memory allocation for such data happens in the heap ā unstructured region of memory. Objects on the heap live on after we exit the function that allocated the memory.
V8 has a built-in Garbage Collector (GC). Garbage collection is a form of memory management. Itās like a collector which attempts to release the memory occupied by objects that are no longer being used.
You can investigate heap by making a snapshot in Chrome Dev Tools.
Since we donāt have access to infinite amount of memory, there is comes V8 built-in Garbage Collector (GC). Garbage collectorās job is to go through objects that are allocated in memory and determine whether they are dead or alive. Those that are alive get to stay in memory, those that are dead get removed, and memory gets allocated back to the heap.
Conclusion
Thanks for reading! I hope that after reading this article you have a better understanding of the mechanics behind JavaScript. These concepts also act as a building block for a greater understanding of other concepts such as scopes and closures. Please, click the clap š, if the article above was helpful for you.