Understanding the Event Loop in JavaScript
Harshit Pandey
React Native | JavaScript | Typescript | Android | IOS | DSA | Node JS
JavaScript is a single-threaded language, which means it executes one task at a time. However, JavaScript efficiently handles asynchronous operations like API calls, timers, and event handling without blocking execution. This is possible due to the Event Loop, a crucial mechanism that manages task execution.
What is the Event Loop?
The event loop is responsible for handling JavaScript's asynchronous behavior. It ensures non-blocking execution by continuously checking the Call Stack, Microtask Queue, and Callback Queue to determine which task should run next.
Example:
console.log("Start");
setTimeout(() => {
console.log("setTimeout Callback");
}, 0);
Promise.resolve().then(() => {
console.log("Promise Resolved");
});
console.log("End");
Output:
Start
End
Promise Resolved
setTimeout Callback
How the Event Loop Works
Key Components:
Phases of the Event Loop
Why is the Event Loop Important?
Common Issues Related to the Event Loop
1. Blocking the Main Thread
A heavy computation can block the event loop, making the app unresponsive:
while(true) {
console.log('Blocking...');
}
2. Delayed Execution of setTimeout
setTimeout may not execute exactly on time if the call stack is busy:
console.log("Start");
setTimeout(() => console.log("Inside setTimeout"), 1000);
for (let i = 0; i < 1e9; i++) {} // Long loop
console.log("End");
Even though setTimeout is set for 1 second, it runs after the long loop finishes.
3. Microtasks Run Before Callbacks
Microtasks (e.g., Promises) run before setTimeout, even if set to 0ms delay:
setTimeout(() => console.log("setTimeout"), 0);
Promise.resolve().then(() => console.log("Promise"));
console.log("End");
End
Promise
setTimeout
Why? The event loop always executes microtasks before callback queue tasks.
4. Callback Hell
Too many nested callbacks make code unreadable:
setTimeout(() => {
console.log("Step 1");
setTimeout(() => {
console.log("Step 2");
setTimeout(() => {
console.log("Step 3");
}, 1000);
}, 1000);
}, 1000);
Solution: Use Promises or async/await to avoid Callback Hell.
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
async function executeSteps() {
await delay(1000);
console.log("Step 1");
await delay(1000);
console.log("Step 2");
await delay(1000);
console.log("Step 3");
}
executeSteps();