This post really aims to cover in a simple way how JavaScript works underneath the hood so that even a new programmer would be capable to grasp the concept and visualize what happens when coding JavaScript.
To start off, there is at least 3 questions I wanted to focus on that will facilitate to overcome difficulties and internalize the logic behind?
These questions are also ones that are likely to be asked during job interviews for web developers where JavaScript implies:
1. How does JavaScript works?
2. Explain the difference between Synchronous vs Asynchronous?
3. Or explain this statement: JavaScript is a single threaded language that can be non –blocking?
Really, It is not necessary to know how JavaScript works internally to write a program but it is essential and crucial to learn in order to understand what is happening behind and feel what you are writing hence some of many developers who have many years in carrier don’t care to know this.
Lets first know what is a program? A program is simply a set of instructions that tells a computer what to do and how to execute the task. The program must allocate the memory, otherwise, we won’t be capable to have variables or even save files on our computers. The program should also parse (read) and execute a dedicated task and all happens in a memory.
Now, JavaScript has engine known as JavaScript engine that each browser implements. For instance in Chrome it is called V8, in Mozilla Firefox: Spider Monkey, Safari browser: JavaScript Core Webkit.
The following image shows the google chrome's V8 engine
What happens inside JavaScript Engine?
JavaScript engine like V8 in chrome reads JavaScript codes that we write and convert them into machine executable instructions for the browser. The above figure is showing parts of JavaScript engine that consists of two parts namely A memory heap **and **A call stack.
It is also important to note that the memory allocation happens in memory heap, while parsing (reading) and execution take place in Call Stack. In addition to this, it is the memory heap that tells you where you are in the program.
Let’s see memory allocation in memory heap with JS (JavaScript) codes
const a = 4; // now we allocated a memory. JS engine is going to remember
// that a has a value of 4.
const Obj = {a, b, c }; // In memory, variable 'Obj' holds the object {a, b,c}
// The same as on array. the engine will remember values of the array
const Array = [1,2,3,4,5]
So, what is an issue with the above code once declared globally?
There is something called Memory Leak. As it was mentioned above, the variable declaration happens in memory heap and it has a limited size to be allocated. When you keep on declaring global variables that are very big arrays instead of numbers and even unused, this fills up the memory and results to the Memory Leak. You will hear that global variables are bad due to when we forget to clean up we fill up this memory heap and eventually the browser won’t be able to work.
What about the Call Stack?
If we remember, it is the Call Stack that reads and execute scripts. Let’s use codes to make it clear.
// Example Call Stak
console.log("x");
console.log("y");
console.log("z");
// Result in browser
// x
// y
// z
With the above codes, the call sack reads the first line console.log(“x”); and gets put into the call stack, the JavaScript engine recognizes that the console.log has been added, then pop it into the call stack, runs it, and outputs x. After, it removes the first console.log as it has finished to run it and place the into the second console.log(“y”), adds it on the Call Stack, execute y and removes the second console.log. Finally gets console.log(“z”) using the same process.
This is the simplest demo, what if a bit more of a complex example? Let’s give a typical example:
// Demo Example
const example1 = () => {
const example2 = () => {
console.log('7');
}
example2();
// Result:
// 4
Now, what happened in the above code according to the call stack? Let’s see how it will run the above block of code :
//CALL STACK
Function example1() will ran first, then function example2() comes on top of the call stack and gets ran which prints out number 7 as output after checking if there is other codes to run. After this, it will start to remove from the call stack in order starting by console.log(‘7’), example2(), example1() and the call stack is now empty.
> Do we remember this statement? JavaScript is a single threaded language that can be non–blocking.
Single threaded means that it has only one call stack. It can perform only one thing at a time and it is important to emphasize that the Call Stack is First In Last Out like a Stack.
Other languages can have many call stacks what is called multi-threaded which could be more advantageous to have multiple call stack so that we don’t keep waiting around for tasks.
> But, why was JavaScript designed to be single threaded though?
To answer this question, normally running code on a single thread can be pretty much easy since there is no complicated scenarios that arise in multi-threaded environment. You actually have one thing to care about. In multi-threaded there could be issues like Deadlocks. With this theory, we can easily know what a Synchronous programming means.
Synchronous programming simply means: first line of code get executed, the second follows and the third gets executed etc …
To be more explicit this means that console.log(“y”) can not run until console.log(“x”) finishes and console.log(“z”) doesn’t start until both first two finish because it is a Call Stack.
It is likely for programmers to use the site stackoverflow.com. what does the name mean? Well. let’s see:
The above image shows how a memory leak can happen and how the memory heap of a JavaScript engine can overflow. Here the call stack receives many inputs greater that it’s size and overflows.
It is possible to demonstrate the stack overflow with the help of code:
// A recursion function that creates a stackoverflow
function foo(){
foo();
}
foo();
// This function will keep looping over and over and keeps adding foo() to
// the call stack and ends up to the stackoverflow.
// Need to have fun? run this in the browser to see.
Mind that JavaScript is a single threaded only one statement is executed at a time . Here is a problem now: what if console.log(“y”) in the following block of code has a big task that will take longer to be executed for example looping through an array that has thousands or millions of items? what would happen there?
// Example Call Stak
console.log("x");
console.log("y");
console.log("z");
// Result in browser
// x
// y
The first line will execute, and assume the second line has a massive job to perform, therefore the third line will wait a long period to get executed. In the above example this doesn’t mean much but let’s think of a big website that perform heavy operations, user would not be able to do anything. The website will freeze until the task is done and the user wait there. This is a bad experience when comes to the performance.
Well, with synchronous tasks, if we have one function that takes much time, it is going to hold up the line. So, it sounds like we need something non-blocking. Remember the statement I mentioned above: JavaScript is a single threaded language that can be non –blocking.
Ideally, in JavaScript we don’t wait around for things that take time. So, how do we go around this problem?
As a rescue, there is Asynchronous programming. So, what is this?
Think of asynchronous like a behavior. Synchronous execution is great because it is predictable. In synchronous we know what happen first, what happen next etc, but it can gets slow.
When we have to do things like image processing or making requests over the network like API calls, we use something more than just synchronous tasks that is Asynchronous.
Let’s see how we can do asynchronous programming with codes:
console.log("x");
setTimeout(() =>{
console.log("y");
},3000);
console.log("z");
// Result
x
z
y // comes after 3 seconds latter
Now, basing on the above codes it seems like we skipped the second line and execute the third and wait on 3 seconds to output the result. that is asynchronous happening.
In order to understand this and what happened let’s use the following figure.
JavaScript Run-Time Environment
For JavaScript to run, we need more than Memory Heap and Call Stack. we need what is called JavaScript Run-Time which is part of the browser. it is included in browsers. On top of the engine, there is something called web APIs, Callback queue and Event loop as shown on figure.
let’s discuss on the code now where we use setTimeout function.
console.log("x");
setTimeout(() =>{
console.log("y");
},3000);
console.log("z");
The setTimeout function is part of web API and not part of JavaScript, instead it is what browsers give us to use to enable us to do asynchronous programming. so, let’s provide more details for clarification.
CALL STACK: The console.log(“x”) goes into the call stack , runs and we console.log to the browser. After that, the setTimeout(() =>{console.log(“y”);},3000);
goes into the call stack because the first task is finished then go to the second one.
Now, here there is a thing, while reading the code, the call stack will detect that there is a setTimeout function that has been set and it is not part of JavaScript but part of web API (See the figure JavaScript Run-Time Environment ) and has it has special characteristic. What happens is that setTimeout triggers the WEB API and because the web API is notified, the function will be popped out of the call stack.
Now, the web API starts a timer of three seconds knowing that in 3 seconds it has to do the task. Remember here, because the call stack is empty, JavaScript engine continues to line 3 which is console.log(“z”); and execute it. That’s why we got result x,z but we have setTimeout three seconds in web API. then, after three seconds when the time limit is up, setTimeout runs and sees what is inside of it and it is done. Once it’s done, the web API will recognizes that it has a callback() function of setTimeout and adds it to the CALLBACK QUEUE being ready to run it.
We come to the last part which is** EVENT LOOP*. This one keeps checking all the time if the call stack is empty. When it is empty and nothing that is currently running in the JavaScript engine, it will check the callback queue and find our **callback()* function with console.log(“z”) then place it in CALL STACK and gets run. Once it’s done, pops it out of the call Stack. Now everything is empty and get the result x z y.
Conclusion: In this post we have seen a lot of information about what happen underneath the hood to completely understand JavaScript logics both tasks that are performed synchronously and Asynchronously.
Hopefully this will help new and advanced JavaScript programmers to enjoy coding in JavaScript related frameworks like ReactJS or AngularJS becouse this is the foundation to start on when understanding advanced logic.
> Happy Coding
References
https://www.freecodecamp.org/news/how-javascript-works-behind-the-scenes.
https://www.simplilearn.com/tutorials/javascript-tutorial/callback-function-in-javascript#
Top comments (0)