The Event Loop in JavaScript serves as a system for coordinating the execution of various code segments, including asynchronous operations such as timers, callbacks, and promises.
JavaScript operates on a single-threaded model. To handle the execution of numerous tasks concurrently without obstructing the primary thread, it is essential to utilize the event loop.
In straightforward terms, the event loop in JavaScript facilitates asynchronous programming by managing tasks in an efficient manner. It also plays a crucial role in preventing asynchronous activities such as API calls, timers, and user interactions from hindering the execution of other code.
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
Explanation
In the code provided, the statement console.log("Start") runs right away. The setTimeout method is utilized to schedule a callback function, but this callback will not be triggered instantly. Conversely, Promise.resolve.then generates a promise that resolves immediately, thereby enabling the execution of the callback function. As a result, console.log("Start") is executed first, followed by console.log("End"), which also executes immediately.
Components of Event Loop
The event loop guarantees that tasks are carried out in the proper sequence, facilitating asynchronous programming. It is composed of two key elements:
Call Stack: Within the context of the JavaScript event loop, the call stack serves the purpose of monitoring all operations that are queued for execution. Each time a function completes its execution, it is removed from the stack.
Event Queue: The event queue is responsible for overseeing the addition of functions to the stack for execution. By employing a queue data structure, it maintains the sequence in which tasks are performed. Asynchronous operations, including timers and events, are stored in the queue awaiting execution until the call stack becomes available.
How does the event loop work?
In JavaScript, the functioning of the event loop can be broken down into several distinct phases, which include the following:
Call stacks: Within the JavaScript event loop, there exists a call stack that oversees the execution of functions following a LIFO (Last In, First Out) methodology.
Web APIs: Within the realm of JavaScript, this encompasses functionalities such as setTimeout, setInterval, fetch, DOM events, along with various other asynchronous processes.
Callback Queue: When an asynchronous task, like a timer, an API request, or an event, becomes ready for execution, it is added to the event queue.
Microtask Queue: Promises, along with other microtasks, are added to the microtask queue, which is handled prior to the task queue.
Event Loop: It perpetually monitors the call stack and, when it finds it to be vacant, transfers tasks from the queue to the stack for processing.
Microtasks vs Macrotasks in the Event Loop
Microtasks: These are tasks that hold a higher priority and are executed prior to macrotasks, which include promises and the function queueMicrotask.
Macrotasks: These tasks are considered to have a lower priority and execute after microtasks. Examples include setTimeout, setInterval, and events related to the Document Object Model (DOM).
Example
console.log("Start");
setTimeout(() => console.log("Macrotask"), 0);
Promise.resolve().then(() => console.log("Microtask"));
console.log("End");
Output:
Start
End
Microtask
Macrotask
Examples
Example 1: setTimeout
Example
console.log("Start");
setTimeout(() => {
console.log("Inside setTimeout");
}, 0);
console.log("End");
Output:
Start
End
Inside setTimeout
Example 2: Promise and microtask
Example
console.log("Start");
Promise.resolve().then(() => {
console.log("Inside Promise");
});
console.log("End");
Output:
Start
End
Inside Promise
Example 3: Promise vs setTimeout
Example
setTimeout(() => {
console.log("Timeout");
}, 0);
Promise.resolve().then(() => {
console.log("Promise");
});
console.log("End");
Output:
End
Promise
Timeout
Phases of the Event Loop
- Timers: During this phase, it executes the callbacks scheduled by setTimeout and setInterval when their respective times expire.
- Pending Callbacks: In this phase, it processes I/O callbacks that have been deferred to the next loop iteration.
- Idle/Prepare: These phases are mainly used internally for bookkeeping and setup.
- Poll Phase: This phase retrieves new I/O events and runs I/O-related callbacks.
- Check Phase: This phase executes the callback registered with setImmediate.
- Close Callbacks: This phase manages clean-up tasks, such as closing connections or terminating servers.
Why is the Event Loop Important?
The event loop enables JavaScript to execute non-blocking input/output operations, which are crucial for contemporary web applications. Let's explore its significance:
Efficient Asynchronous Handling
In JavaScript, the event loop oversees tasks like retrieving data, handling user input, or interacting with timers, all while ensuring that other sections of the code can run without interruption.
User Interactions
In JavaScript, the implementation of the event loop allows us to manage various user interactions concurrently while executing background tasks without causing any lag in the user interface.
Single-Threaded
The event loop adeptly manages several operations by assigning tasks to the task queue and executing them in a sequence that does not obstruct the main thread.
Conclusion
In conclusion, the event loop in JavaScript serves as a system that allows a single-threaded programming language to handle asynchronous operations without hindering the primary thread. It functions in conjunction with the call stack, which processes synchronous code, and the event queue, where asynchronous tasks are lined up for execution.