DEV Community

Cover image for What is the JS Event Loop and Call Stack?
Neeraj Kumar
Neeraj Kumar

Posted on

What is the JS Event Loop and Call Stack?

The Event Loop is a source of confusion for many developers, but it's a fundamental piece of the JavaScript engine. It's what allows JavaScript to be single-threaded, yet able to execute in a non-blocking fashion. To understand the Event Loop, we first need to explain a few things about the JavaScript engine, such as the Call Stack, Tasks, Microtasks and their respective Queues. Let's break them down one by one.
The Call Stack
The Call Stack is a data structure that keeps track of the execution of JavaScript code. As the name suggests, it's a stack, thus a LIFO (Last In, First Out) data structure in memory. Each function that's executed is represented as a frame in the Call Stack and placed on top of the previous function.

Regular Event Loop

This shows the execution order given JavaScript's Call Stack, Event Loop, and
any asynchronous APIs provided in the JS execution environment example;
Web APIs in a Browser environment.


Given the code

setTimeout(() => { 
  console.log('Neeraj')
}, 1000)           
Enter fullscreen mode Exit fullscreen mode

The Call Stack, Event Loop, and Web APIs have the following relationship

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  ------------------------|-------------------|--------------| |-------------------------|
  setTimeout(() => {      |                   |              | |                         |
    console.log('Neeraj') |                   |              | |                         |
  }, 1000)                |                   |              | |                         |
                          |                   |              | |                         |
Enter fullscreen mode Exit fullscreen mode

To start, everything is empty


        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | <global>          |              | |               |
console.log('Neeraj') |                   |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |
Enter fullscreen mode Exit fullscreen mode

It starts executing the code, and pushes that fact onto the Call Stack (here named
<global>)


        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
> setTimeout(() => {  | <global>          |              | |               |
console.log('Neeraj') | setTimeout        |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |
Enter fullscreen mode Exit fullscreen mode

Then the first line is executed. This pushes the function execution as the
second item onto the call stack.

Note that the Call Stack is a stack; The last item pushed on is the first
item popped off. Aka: Last In, First Out. (think; a stack of dishes)


        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
> setTimeout(() => {  | <global>          |              | | timeout, 1000 |
console.log('Neeraj') | setTimeout        |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |
Enter fullscreen mode Exit fullscreen mode

Executing setTimeout actually calls out to code that is not part of JS.
It's part of a Web API which the browser provides for us.
There are a different set of APIs like this available in node.


        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | <global>          |              | | timeout, 1000 |
console.log('Neeraj') |                   |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |
Enter fullscreen mode Exit fullscreen mode

setTimeout is then finished executing; it has offloaded its work to the Web
API which will wait for the requested amount of time (1000ms).


        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  |                   |              | | timeout, 1000 |
console.log('Neeraj') |                   |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |
Enter fullscreen mode Exit fullscreen mode

As there are no more lines of JS to execute, the Call Stack is now empty.


        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  |                   | function   <-----timeout, 1000 |
console.log('Neeraj') |                   |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |
Enter fullscreen mode Exit fullscreen mode

Once the timeout has expired, the Web API lets JS know by adding code to the
Event Loop.

It doesn't push onto the Call Stack directly as that could intefere with already
executing code, and you'd end up in weird situations.

The Event Loop is a Queue. The first item pushed on is the first
item popped off. Aka: First In, First Out. (think; a queue for a movie)


        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | function        <---function     | |               |
console.log('Neeraj') |                   |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |
Enter fullscreen mode Exit fullscreen mode

Whenever the Call Stack is empty, the JS execution environment occasionally checks
to see if anything is Queued in the Event Loop. If it is, the first item is moved
to the Call Stack for execution.


        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | function          |              | |               |
console.log('Neeraj') | console.log       |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |
Enter fullscreen mode Exit fullscreen mode

Executing the function results in console.log being called, also pushed onto
the Call Stack.


        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | function          |              | |               |
console.log('Neeraj') |                   |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |
> Neeraj
Enter fullscreen mode Exit fullscreen mode

Once finished executing, Neeraj is printed, and console.log is removed from the
Call Stack.


        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  |                   |              | |               |
console.log('Neeraj') |                   |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |
> Neeraj
Enter fullscreen mode Exit fullscreen mode

Finally, the function has no other commands to execute, so it too is taken off
the Call Stack.

Our program has now finished execution.

End.

Top comments (0)