EventJavaScript Event Loop
JavaScript is a single-threaded language, meaning it can handle only one task at a time. When a piece of code takes longer to execute, it causes the execution flow to halt, waiting for the task to complete — this is known as code blocking. To prevent blocking, JavaScript leverages asynchronous tasks and APIs (like setTimeout
, Promises, etc.).
The JavaScript Event Loop consists of four major components:
- Call Stack
- Web Apis
- Microtask Queue
- Macrotask Queue
When JavaScript code runs, it executes line by line. If it encounters an asynchronous task, that task is delegated to the Web API, allowing the synchronous code to continue executing.
Tasks Handled by Each Component
Call Stack:
The call stack is responsible for keeping track of the currently executing functions. It handles:
- Execution of all synchronous code (e.g., function calls, variable declarations)
- Function invocations (e.g.,
foo()
) - Managing the order of execution based on the Last In, First Out (LIFO) principle
Web APIs:
These are browser-provided features that handle asynchronous tasks. Some common Web APIs include:
setTimeout
andsetInterval
- DOM events (e.g.,
click
,keydown
) - AJAX requests (e.g.,
fetch
,XMLHttpRequest
) - Geolocation API
- WebSocket API
- File API (e.g.,
FileReader
) requestAnimationFrame
- Media APIs (e.g.,
getUserMedia
)
Microtask Queue:
Microtasks are usually small, quick tasks that need to be executed after the currently executing script. Tasks handled by the microtask queue include:
- Promise callbacks (e.g.,
.then
,.catch
) queueMicrotask
- MutationObserver callbacks
Macrotask Queue:
Macrotasks are tasks that can take longer to execute and are scheduled for execution after all the microtasks have been processed. Tasks handled by the macrotask queue include:
setTimeout
andsetInterval
callbacks- I/O tasks (e.g., reading files, network events)
- UI rendering tasks
requestAnimationFrame
callbacks
How the Event Loop Works
- When JavaScript code runs, the synchronous code is executed first. If an asynchronous task is encountered, it is moved to the Web API, and the synchronous code continues to execute. Once all synchronous tasks are done, the Event Loop starts processing tasks from the queues.
- It starts from microtask queue, once all the tasks in micro queue gets completed then only it starts macrotasks queue.
- Also for the each task in macro queue gets completed, it will the micro queue before moving to the next macro task.
Example:
console.log('1');
setTimeout(() => console.log('2'), 0);
console.log('3');
console.log('4');
Output:
1
3
4
2
In this example, 3
and 4
are logged before 2
because the setTimeout
callback is moved to the Web API and only executed after all synchronous tasks are completed.
Key Points to Understand
- Asynchronous tasks are handled by the Web API, and their callbacks are executed only when they return to the execution queue.
Example 1:
setTimeout(() => {
// Some logic here
}, 3000);
In this case, the timeout waits for 3 seconds in the Web API without executing the logic. After the 3 seconds, the callback is moved to the macrotask queue, where it will be executed when the call stack is clear.
Example 2:
function getResult() {
return new Promise((resolve, reject) => {
// Some logic here
if (/* logic */) {
resolve(callback());
} else {
reject("Error: '...'");
}
});
}
Here, the promise’s resolve/reject logic is handled by the Web API. Once completed, it is moved to the microtask queue. The callback function is executed only when it reaches the call stack.
Important Notes
- Microtasks are executed before macrotasks. For every completed task in the macrotask queue, the event loop checks the microtask queue and executes any pending microtasks before moving to the next macrotask.