Event Loop
JavaScript is a synchronous, single-threaded language. It operates with one call stack, which is part of the JavaScript engine, and can execute only one task at a time. All JavaScript code is executed within this call stack.
When code is run, a Global Execution Context (GEC) is created and placed in the call stack. Any task added to the call stack is executed immediately. However, certain tasks, like setTimeout
or network requests, cannot be handled directly by the call stack because it lacks built-in mechanisms like timers or network handling capabilities.
For such asynchronous tasks, JavaScript relies on Web APIs provided by browsers. These APIs enable functionalities like setTimeout
, DOM manipulation, fetch()
, localStorage
, console
, and location
.
Web APIs are accessible via the window
object in the browser. For example, calling window.setTimeout
allows you to use the setTimeout
function. However, you typically don’t need to prefix these features with window
because the window
object is global. This means its properties and methods, like setTimeout
, are directly accessible without explicitly referencing window
.
In summary, the browser wraps additional features in the window
object and provides them as interfaces that work alongside the JavaScript call stack. This seamless integration enables JavaScript to handle both synchronous and asynchronous tasks effectively.
CASE 1
setTimeout
and Console Execution
Consider the following code:
console.log("Start");
setTimeout(function cb() {
console.log("callback");
}, 5000);
console.log("End");
This code demonstrates how the JavaScript event loop works in conjunction with setTimeout
. Below is the step-by-step breakdown:
Step-by-Step Execution
console.log("Start")
ExecutionThe
console.log("Start")
statement is added to the Call Stack.It executes immediately, logging
"Start"
to the browser console.After execution, it is removed from the Call Stack.
setTimeout
ExecutionWhen
setTimeout
is encountered:It requests access to the browser's Timer feature.
The callback function (
cb
) is registered in the browser's Web APIs environment.The timer begins counting down for
5000ms
.
console.log("End")
ExecutionThe next statement,
console.log("End")
, is added to the Call Stack.It executes immediately, logging
"End"
to the browser console.After execution, it is removed from the Call Stack.
Call Stack Becomes Empty
- At this point, both
console.log
statements have executed, and the Global Execution Context (GEC) is cleared from the Call Stack.
- At this point, both
Timer in the Web APIs
While the above steps occur, the
setTimeout
timer is running asynchronously in the Web APIs section.Once the
5000ms
timer is complete, the callback function (cb
) is moved to the Callback Queue.
Callback Queue and Event Loop
The Callback Queue holds the
cb
function until the Call Stack is empty.The Event Loop continuously monitors the Call Stack. As soon as it detects the Call Stack is clear, it pushes the callback function (
cb
) from the Callback Queue into the Call Stack for execution.
Callback Execution
The
cb
function is added to the Call Stack.It executes, logging
"callback"
to the browser console.After execution, it is removed from the Call Stack.
Execution Order in the Browser Console
Start
End
callback
CASE 2
Understanding DOM APIs and Console Execution
Consider the following code:
console.log("Start");
document.getElementById("btn")
.addEventListener("click", function cb() {
console.log("Callback");
});
console.log("End");
This code demonstrates how JavaScript interacts with the DOM and the event loop through addEventListener
. Below is a step-by-step explanation:
Step-by-Step Execution
console.log("Start")
ExecutionThe
console.log("Start")
statement is added to the Call Stack.It executes immediately, logging
"Start"
to the browser console.After execution, it is removed from the Call Stack.
addEventListener
ExecutionWhen
addEventListener
is called:It accesses the DOM API, which is provided by the browser as part of the
window
object.The DOM API parses the HTML source code to locate the element with the ID
"btn"
.The callback function (
cb
) is registered in the Web APIs and associated with theclick
event of thebtn
element.
console.log("End")
ExecutionThe next statement,
console.log("End")
, is added to the Call Stack.It executes immediately, logging
"End"
to the browser console.After execution, it is removed from the Call Stack.
Callback Registration in Web APIs
The callback function registered in the Web APIs is now waiting for the
click
event on thebtn
element.This registration persists until either:
The browser/tab is closed, or
The event listener is explicitly removed.
Button Click and Callback Execution
When the button (
btn
) is clicked:The browser triggers the associated
click
event.The registered callback function (
cb
) is moved to the Callback Queue.
Event Loop and Callback Execution
The Event Loop continuously monitors the Call Stack.
As soon as the Call Stack is empty, the
cb
function is moved from the Callback Queue into the Call Stack.The
cb
function is then executed, logging"Callback"
to the browser console.After execution, the
cb
function is removed from the Call Stack.
Execution Order in the Browser Console
The browser console will display the following output in order:
Start
End
// "Callback" will appear only after the button is clicked
Understanding fetch()
, setTimeout
, and Console Execution
Consider the following code:
console.log("Start");
setTimeout(function cBT() {
console.log("CB Set Time out");
}, 5000);
fetch("https://api.netflix.com")
.then(function cbF() {
console.log("CB Netflix");
});
console.log("End");
Step-by-Step Execution
console.log("Start")
ExecutionThe
console.log("Start")
statement is added to the Call Stack.It executes immediately, logging
"Start"
to the browser console.After execution, it is removed from the Call Stack.
setTimeout
ExecutionThe
setTimeout
function is encountered:It accesses the Timer feature of the browser.
The callback function (
cBT
) is registered in the Web APIs, and a timer is started for5000ms
.
fetch()
ExecutionThe
fetch
function initiates an API request tohttps://api.netflix.com
:The Web APIs handle the network request asynchronously.
When the response is received from the API server, the
.then()
callback (cbF
) is registered in the Microtask Queue.
console.log("End")
ExecutionThe next statement,
console.log("End")
, is added to the Call Stack.It executes immediately, logging
"End"
to the browser console.After execution, it is removed from the Call Stack.
Processing the
Microtask Queue
Once the Call Stack is empty, the Event Loop gives priority to the Microtask Queue over the Callback Queue.
The
cbF
function (from thefetch
promise) is moved to the Call Stack and executed:"CB Netflix"
is logged to the browser console.
After execution,
cbF
is removed from the Call Stack.
Processing the
Callback Queue
After the Microtask Queue is emptied, the Event Loop moves to the Callback Queue.
Once the
5000ms
timer completes, thecBT
function is moved from the Callback Queue to the Call Stack and executed:"CB Set Time out"
is logged to the browser console.
After execution,
cBT
is removed from the Call Stack.
Execution Order in the Browser Console
The browser console will display the following output in this order:
Start
End
CB Netflix
CB Set Time out
Key Points
setTimeout
:- Registers its callback in the Web APIs and schedules it to move to the Callback Queue after the timer expires.
fetch()
:Uses the Web APIs to handle the API request.
When the response is received, its callback is placed in the Microtask Queue, which has a higher priority than the Callback Queue.
Event Loop Priorities:
- The Event Loop processes tasks from the Microtask Queue before the Callback Queue, ensuring promises and other asynchronous operations are resolved sooner.
Call Stack:
- Tasks are always executed from the Call Stack, whether they originate from the Microtask Queue or the Callback Queue.
What is Prioritized in the Microtask Queue?
Promise Callbacks (
.then
,.catch
,.finally
)- Promises are one of the most common sources of microtasks. When a promise is resolved or rejected, its associated
.then
,.catch
, or.finally
callback is added to the microtask queue.
- Promises are one of the most common sources of microtasks. When a promise is resolved or rejected, its associated
MutationObserver
Callbacks- The
MutationObserver
API, used to detect DOM changes, places its callbacks in the microtask queue for efficient handling.
- The
Queue Microtask API (
queueMicrotask
)- Developers can explicitly add a task to the microtask queue using the
queueMicrotask()
function.
- Developers can explicitly add a task to the microtask queue using the