DEV Community

Mayank Vats
Mayank Vats

Posted on

Javascript: Under the hood

    One day, a curious guy named Thomas, who is a Software Developer, was wondering What does the execution context means? What actually happens when the JavaScript program first runs? If JavaScript is single threaded then how does it work asynchronously?

    He then shifted himself into learning mode and started searching for the answers and wrote about all his learning, which I am going to share here.

🚀 The First Run

        Whenever we write the JavaScript code and execute it in our browser or in NodeJs, following things happen:

  • The compiler goes throw the whole source code, assigning memory to the variables, storing function definitions and creating Global Execution Context (Creation Phase).
  • Then, it assigns the values to the variables, makes execution context on each function call, and also pushes the function onto the call stack (Execution Phase)

Let's take an example:

var message = "Hello World!";

const send = function (message) {
  const log = `"${message}" sent to the reciever`;
  console.log(log);
};

send(message);
Enter fullscreen mode Exit fullscreen mode

    For the code above, firstly the compiler reads through it and decides which variables need memory and what function definitions needs to be stored, this is the Creation Phase.

    Secondly, the compiler goes through the code again and this time it assigns the values to the variables and looks for the function calls to create an Execution Context.

Execution Context

    In the image above GEC is the Global Execution Context, which is always at the base of the call stack, you may have seen something like <anonymous> in the browser's console.

    Similarly, when the send function is called, the creation and execution phases are carried out and an execution context is created.

    Execution Context can be thought of as an environment or a place where the current code execution is taking place.

    So, whenever the JavaScript code runs, very first execution context that is the Global Execution Context is created, this Global Execution Context is the place which is responsible for storage and execution of the rest of the code, then on every function call a new execution context gets created, which may also refer to its parent execution context.

🚀 Synchronous Nature

 JavaScript is a single threaded programming language, that means, JavaScript engine has only one call stack and one memory heap. Due to the presence of only single call stack it can run single instruction at a time.

Call stack and Heap

    Earlier, JavaScript was used for only dynamic rendering of elements in the web pages and form validation. These tasks required only single thread to run on. But, as the technology progressed and web applications became more complex, more long running tasks such as an API call, querying the database, prompting user to upload a file, became headache with this synchronous flow.

To mimic this synchronous and blocking nature, Open your browser's console and type alert() and press enter.

🚀 Asynchronous Nature

    JavaScript got its asynchronous nature with the help of its runtime, which consists of a JavaScript Engine, Web APIs, callback queue and an event loop.

JavaScript Runtime

JavaScript Engine, consists of Call Stack and Memory Heap, which are responsible for execution of the JavaScript code.

Call Stack, keeps record of the current running function as well as the functions that needs to be executed once the current function completes its execution and is popped off the stack.

Callback Queue, is a queue which keeps record of the functions(or processes) that needs to be executed by the engine.

Event Loop, firstly checks if Call Stack is empty, monitors the Callback Queue and checks for any DOM events or processes in the queue that needs to be executed by pushing onto the stack.

Combination of these is what gives JavaScript its asynchronous capabilities. Let's go through an example:

console.log("Fetching Data");

function sayHello() {
  console.log("JS says Hello!");
}

function fetchDatabase() {
  console.log("Data Retrieved");
}

setTimeout(() => {
  fetchDatabase();
}, 3000);

sayHello();
console.log("Meanwhile doing some other task...");
Enter fullscreen mode Exit fullscreen mode

If you run the above code in the browser or in the node, the output will be:

Fetching Data
JS says Hello!
Meanwhile doing some other task...
Data Retrieved
Enter fullscreen mode Exit fullscreen mode

Following things happened when the above code is run:

  • JavaScript printed the first statement.
  • After that, it called the Web API that is setTimeout() and delegated the task of fetching from the database.
  • Then the sayHello() function gets pushed on to the Call Stack and the second statement gets printed and sayHello() is popped off the stack.
  • Meanwhile the Callback Queue keeps record for some other pending tasks or DOM events and Event Loop continuously checks for the tasks in the queue and pushes the task onto the Call Stack.
  • The third statement gets printed, and after 3 seconds the data is retrieved and Event Loop pushes it onto the Call Stack and the last statement gets printed.

    Here you saw that how setTimeout() simulated a long running task of fetching from database and the asynchronous nature of the runtime made the code Non-blocking.

In this post you got an idea about:

  1. Execution Context and the two phases
  2. Single threaded nature of JavaScript
  3. Call Stack and Memory Heap
  4. JavaScript Runtime, event loop, callback queue

Keep Learning and Keep Growing 🎉

Oldest comments (0)