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)
The Call Stack, Event Loop, and Web APIs have the following relationship
[code] | [call stack] | [Event Loop] | | [Web APIs] |
------------------------|-------------------|--------------| |-------------------------|
setTimeout(() => { | | | | |
console.log('Neeraj') | | | | |
}, 1000) | | | | |
| | | | |
To start, everything is empty
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | <global> | | | |
console.log('Neeraj') | | | | |
}, 1000) | | | | |
| | | | |
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) | | | | |
| | | | |
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) | | | | |
| | | | |
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) | | | | |
| | | | |
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) | | | | |
| | | | |
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) | | | | |
| | | | |
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) | | | | |
| | | | |
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) | | | | |
| | | | |
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
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
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)