Event Looping in JS — Part 1

Gurinderpal Singh Narang
6 min readJan 18, 2024

--

The Event Loop is a continuous process that handles the execution of code, managing the call stack, microtask queue, and callback queue to ensure efficient and non-blocking execution of asynchronous operations in JavaScript.

To grasp the concept of the Event Loop, let’s begin by understanding the inner workings of JavaScript code. Let’s explore the following fundamental elements that actively contribute to this process:

  1. Memory Heap
  2. Global Execution Context(GEC)
  3. Call Stack
  4. Task Queue — Callback Queue + Micro Task Queue

Memory Heap

In JavaScript, the memory heap is a region of memory where the runtime environment (like a web browser or Node.js) allocates space for variables, objects, and functions during the execution of a program.

Here are some key points about the memory heap in JavaScript:

  1. Dynamic Memory Allocation: The heap is responsible for dynamically allocating memory for objects and data structures that are created during the program’s execution. This contrasts the stack, which handles static memory allocation for function calls and local variables.
  2. Objects and Variables: Objects, arrays, and other complex data structures are stored in the heap. When you create an object in JavaScript, such as through the new keyword or object literals, the memory for that object is allocated to the heap.
  3. Garbage Collection: JavaScript uses automatic garbage collection to manage memory in the heap. Garbage collection identifies and frees up memory that is no longer in use, helping to prevent memory leaks. The process involves identifying unreferenced objects and reclaiming their memory.
  4. Reference Counting: One of the techniques used for garbage collection in JavaScript is reference counting. Each object in the heap has a reference count, and when an object’s reference count drops to zero (meaning it is no longer reachable from the program), the memory for that object is freed.
  5. Memory Leaks: While garbage collection helps manage memory, it’s still possible to unintentionally create memory leaks in JavaScript. This can happen when references to objects persist longer than needed, preventing them from being collected by the garbage collector.

Understanding how the memory heap works is important for writing efficient JavaScript code, especially when dealing with large-scale applications or long-running processes. Developers should be mindful of object lifetimes, manage references carefully, and be aware of potential memory leak scenarios.

Global Execution Context(GEC):

Everything in Javascript happens inside an Execution Context. Whenever a JS code is executed a Global execution context is created.

The Global Execution Context (GEC) is the outermost context or scope in which code is executed. When a JavaScript program runs, the JavaScript engine creates the Global Execution Context to manage the global scope. While creating GEC there are two phases:

Initialization Phase/Creation Phase: During the creation of the Global Execution Context, a creation phase occurs where variable and function declarations are hoisted, and memory space is allocated for them. Variables are initialized with undefined during this phase.

Execution Phase: After the initialization phase, the actual code is executed in a top-to-bottom manner. Assignments and function calls are performed during this phase.

Call Stack:

In JavaScript, the call stack is a data structure that keeps track of function calls in a program. It operates on a Last In, First Out (LIFO) basis, meaning that the last function that is called is the first one to be removed. When a function is called, a new frame is pushed onto the call stack, and when the function returns, its frame is popped off the stack.

Let’s discuss this with the following code and understand how it gets executed and pushed to the call stack:

function firstfunction() {
secondFunction();
console.log("Inside firstFunction");
}

function secondFunction() {
thirdFunction();
console.log("Inside secondFunction");
}

function thirdFunction() {
console.log("Inside thirdFunction");
}

firstFunction();

1. All the function declarations in the memory heap take memory during the creation/initialization phase.

2. After the initialization phase, during the execution phase firstFunction() , it gets called.

3. Initially, our call stack had (anonymous)/main() function ,

but oncefirstFunction() It gets called and pushed to the empty call stack, which is the trigger point for the above code.

4. Inside firstFunction(), the very first statement is secondFunction(), which is called secondFunction()and after that secondFunction() is pushed to the call stack, and the call stack becomes:

5. Inside secondFunction(), thirdFunction() is called and the call stack becomes:

6. Inside thirdFunction(), the “console.log()” statement is executed and after that, nothing else is there in the code to execute. Hence the function ends there and gets removed from the top of our call stack. and our call stack becomes firstFunction() -> secondFunction()

7. Now secondFunction() is there at the top and it is being executed by the call stack and, the “console.log()” statement inside secondFunction() is executed. Once execution is done for secondFunction(), it’s also removed from the call stack. and only firstFunction() is left in the call stack.

8. The same goes for firstFunction() and after the end of execution, it is removed from the call stack. and again our call stack goes empty.

Task Queues:

In JavaScript, the task queue is part of the event loop mechanism, which is crucial for handling asynchronous operations. The event loop is a process that continuously checks the message queue for new events and executes them in a sequential order. The task queue is one of the queues within this event loop.

Here’s how the task queue fits into the broader concept:

  1. Call Stack: The call stack is a data structure that keeps track of the currently executing functions in the program. When a function is called, it is pushed onto the stack, and when it completes, it is popped off the stack.
  2. Web APIs and Callbacks: When you perform asynchronous operations in JavaScript, such as making a network request, the browser’s Web APIs handle these operations outside the JavaScript runtime. Once these operations are completed, a callback function is placed in the task queue.
  3. Task Queue: The task queue is a queue that holds callback functions and events to be processed by the event loop. Callbacks in the task queue are processed after the call stack is empty. This ensures that long-running synchronous operations don’t block the execution of other tasks. These tasks can include not only callbacks from asynchronous operations (such as setTimeout, AJAX requests, or event handlers) but also other types of tasks.
  4. Micro-task Queue/Job Queue: This queue, sometimes called the “Job Queue,” is a specific type of queue that contains tasks that are executed at the end of each task in the call stack. Microtasks typically include callbacks from promises (then and catch), process.nextTick in Node.js, and the MutationObserver API.

In Summary, understanding how these components work together provides a foundation for comprehending the Event Loop, a crucial concept in JavaScript that ensures your code can handle asynchronous tasks effectively.

Reference taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop

Thanks for Reading!!!

--

--