How JavaScript code works: Execution context, callStack, 
 async Js, event loop, web API's  callback Queue

How JavaScript code works: Execution context, callStack, async Js, event loop, web API's callback Queue

1.Execution Context

What is an Execution Context?
Everything in JS happens inside an Execution Context.
Execution context is like a big box, it has 2 components.
1. Memory Component 2. Code Component
In the memory component, all variables and functions are stored as a key-value pair.
And in the code component, the code is executed line by line from top to bottom.

When a JavaScript program is run, a global execution context is created. It creates in two phases
1. Memory Creation Phase
In the Memory Creation phase, the Javascript engine allocates memory to every variable and function.

And when it allocates memory for variables it stores undefined in that variable.
And in the case of a function, it stores the whole code of the function.

2. Code Execution phase
Code Execution phase, It is the phase where the JavaScript engine runs again through the whole program and executes code now.


example:

var n=2;

function square(num){
    var ans = num * num;
    return ans;
}

var square2 = square(n);
var square4 = square(4);

In this example,
In phase 1 (memory creation) the memory will allocate to every variable and function, and we saw already when it allocates memory for variables it stores undefined in that variable. And in the case of a function, it stores the whole code of the function.

How this code will be executed behind the scene?

In phase 1 (memory creation) the memory will be allocated to every variable and function.

Here we can see In phase1 the memory was allocated to every variable and function, And it stored undefined to the variables, and in the case function, it stored the whole code of the function.

In phase 2 the code will execute line by line top to bottom.

  1. Line 1: n will be 2

  2. Line 2-6: nothing to do

  3. Line 7: Here we are calling a function
    Whenever a function is called a new execution context is created. The function code executes inside the function execution context, It has 2 components and creates in 2 phases like the global execution context.

In phase 1 of the Function Execution Context, the memory will be allocated to every variable and function which is inside the function like the global execution context.

when javaScript engine saw function calling, JS engine will go inside that function and execute the code of that function line by line from top to bottom

In phase 2 of the Functional Execution Context, the code will execute line by line from top to bottom.
And after executing line number 7 it'll look like this:

When it sees the return, it stops the execution of the function and returns the control back to where the function is called. In this case, the value of ans will be returned to square2.

After completing the execution of the function then the execution context of that function will be deleted.

  1. Line 7: There is again function call then it'll exactly work as line number 6,

Now program is ended

And once the program is ended the global execution context is also deleted.


2. Call stack

The call stack Maintains the Order of Execution of the Execution Context. JavaScript is a single-threaded language so, it has one call stack.
At the bottom of the call stack each time we have our global execution context.
The whole execution context is pushed inside the call stack.
Whenever any JavaScript program executes, the Global Execution Context is at the bottom of the call stack.
Global Execution Context is also known as main.

And whenever a function is invoked and a new function execution context is created, that function execution context is put inside the top of the call stack.
And once the function has done its execution, the function is popped out from the call stack.

example :

const second = () => {
  console.log('Hello there!');
}
const first = () => {
  console.log('Hi there!');
  second();
  console.log('The End');
}
first();

How is it working?

When the JavaScript engine executes this code, it places the global execution context (denoted by the main() or global()) function on the call stack.
So the main() was pushed inside the call stack.

  1. first() function was called and pushed to the top of the call stack.

  2. console.log('Hii there!') is pushed into the stack and popped out from the stack after execution.

  3. The second() function was called and pushed inside the call stack.

  4. console.log('Hello there!') was pushed to the top of the stack, and after execution, it popped out from the stack,

  5. second() function was ended so it popped out from the stack.

  6. console.log("The End"); pushed inside the stack and after execution popped out from the stack.

  7. first() function was done with it work, and it popped out from the stack.

  8. The program was ended and main() popped out from the stack.

  9. And at the end, the call stack is empty.


Until Now we learned execution context and call stack, Synchronously. synchronous - JS engine only goes to the next line, once the current line has finished execution.

Now, Let's see Asynchronous JavaScript.

Asynchronous JavaScript helps us from blocking the code.
What is Blocking of code?
The JavaScript engine can't go to the next line until the current line has finished execution.
example:

console.log("1");
hugeFunction();
console.log("2");

console.log("1") will push inside call stack and after execution it will pop from the stack.

Next, the hugeFunction() will be called and push inside call stack.
suppose hugeFunction() has a thousand lines of code, so this function will take time to execute, so it will block the execution of the program until hugeFunction() finishes its execution.
But until hugeFunction() done its execution it will not pop from the stack and next line will not execute i.e console.log("2"); How can we solve this problem?

We can solve this problem using Web APIs, callback Queue, and event loop.

example :

console.log(1);
setTimeout(() =>{
    console.log(3);
},0);
console.log(2);

Here I have used the setTimeout method to make JavaScript Asynchronous. iIt not a part of the JavaScript engine, it’s part of the browser known as web APIs.

How does it work?

  • Global Execution Context is pushed inside call stack.

  • console.log(1); pushed inside call stack, and after execution popped out from call stack.

  • setTimeout() pushed inside the call stack, then pushed into webAPI environment with a given delay. Wait till the given time expires.

  • setTimeout() timer ended and pushed inside the callback/event queue.

  • The event loop will check, is the call stack empty, in this case not yet.

  • console.log(2); pushed inside call stack, and after executing popped out from call stack.

  • The program ended and the global execution context main() popped out from the call stack.

  • The call stack is empty now.

  • The event loop keeps checking call stack is empty or not, now it is empty so it pushed that callback() in the call stack and the call stack executes that callback() function line by line.

Event Queue: It is the place where the callback function gets pushed to, and waits for the execution.

Event Loop: This is a constantly running process that checks if the call stack is empty. Every time it looks at the call stack and if it is empty it looks into the Event Queue. If there is something in the event queue that is waiting it is moved to the call stack. If not nothing happens.

Conclusion :

So we learned how JavaScript works behind the scene, When the JavaScript program runs The cycle keeps running from Call Stack to web APIs, web API's to Event Queue, and Event Queue to Call Stack through the Event Loop. It is the process of how JavaScript works behind the scene.