Understanding the Event Loop in JavaScript
Discover how the Event Loop works in JavaScript and why it is fundamental for asynchronous programming.
Published:Introduction
JavaScript is a single-threaded language, which means that operations are executed one at a time during the execution of a program, using a single Call Stack.
But what happens when we have laborious operations that take a long time to execute? Imagine a network call that takes 5 seconds to complete. This would block our application, preventing other operations from being executed. This is where the event loop comes into play.
Operations that do not originate directly from JavaScript, but from the browser or Node.js, are called Web APIs in the context of the browser and Node.js APIs in the context of Node.js. Examples include fetch
calls, setTimeout
, and file read/write operations. These operations are delegated to their respective APIs, and once completed, their callbacks are placed in the Task Queue, allowing the Call Stack to remain free for other functions, keeping our application responsive.
Let's take a detailed look at the individual components of the event loop.
Call Stack
The Call Stack is a data structure that manages the order of execution of functions in JavaScript. When a function is called, it is added to the top of the Call Stack. Once completed, it is removed. This mechanism allows JavaScript to execute one function at a time, maintaining the correct order of execution and ensuring the logical flow of the program.
Web APIs
Web APIs are interfaces provided by the browser to perform asynchronous operations such as network calls, timers, and DOM manipulation. These operations are delegated to Web APIs, which handle them separately from the JavaScript Call Stack. Once completed, the callbacks of Web APIs are placed in the Task Queue, from where the event loop moves them to the Call Stack for execution. This allows JavaScript to remain responsive and handle asynchronous operations without blocking the execution of the program.
Task Queue
The Task Queue, or Callback Queue, is a queue where the callbacks of asynchronous operations are placed once the Web APIs have completed them. When the Call Stack is empty, the event loop moves the first callback from the Task Queue to the Call Stack for execution. This mechanism allows JavaScript to efficiently manage asynchronous operations, ensuring that the main functions are not blocked by long operations.
Microtasks
In addition to the Task Queue, there is also the Microtask Queue, which has a higher priority. Microtasks include promises and MutationObserver. Microtasks are executed immediately after the current operation and before the event loop continues with the Task Queue.
Event Loop
The event loop is a continuous cycle that checks if the Call Stack is empty. If the Call Stack is empty and there are callbacks in the Task Queue, the event loop moves the first callback from the Task Queue to the Call Stack, allowing it to be executed.
Example
console.log('Start');
setTimeout(() => {
console.log('Timeout expired');
}, 0);
Promise.resolve().then(() => {
console.log('Promise resolved');
});
console.log('End');
Explanation
1. Synchronous: console.log('Start')
andconsole.log('End')
are executed immediately.
2. Microtask: The promise (Promise.resolve().then()
) is handled right after the synchronous code.
3. Macrotask: setTimeout()
is handled after all microtasks are completed.
Output:
Start
End
Promise resolved
Timeout expired
In short, the event loop
executes synchronous code first, then promises (microtasks), and finally timers (macrotasks).