What Is an Event Loop in JavaScript?
February 14, 2023
Event loop is undoubtedly one of the classic interview questions and there are many related types of questions that one should prepare for. It is important for readers to have a certain degree of understanding of this concept. In this article, we will summarize the basic concepts of the event loop in JavaScript. If you want to practice simulation questions related to the event loop, you can go to the article "The Most Common JavaScript Event Loop Interview Questions".
Synchronous and Asynchronous Operations in JavaScript
Synchronous and Asynchronous Operations in JavaScript Before discussing the event loop, it is important to understand the concepts of synchronous and asynchronous operations. JavaScript is a single-threaded programming language, which means that it executes one line of code at a time, a concept known as synchronous。
However, this can present a problem. Consider a scenario where a website needs to retrieve data from a server, but it takes ten seconds to do so. During the wait time, the website cannot perform any actions. From a user's perspective, the page appears to be frozen for ten seconds, creating a poor user experience. This is where the concept o asynchronous comes into play.
Asynchronous code or events do not block the main thread from executing other code. Using the previous example of a website retrieving data from a server, retrieving the data is an asynchronous event. The asynchronous event will notify the main thread after completion, allowing the main thread to continue executing other code, and allowing user interactions to continue uninterrupted. The ability for browsers or other execution environments (such as Node.js) to implement asynchronous operations is due to the mechanism of the event loop. The event loop mechanism effectively solves the single-threaded problem in JavaScript, ensuring that time-consuming operations do not block the main thread.
Components of the Event Loop - Stack and Task Queue
The event loop itself does not exist in JavaScript but is instead implemented by the JavaScript execution environment (browser or Node.js). There are several concepts involved, including:
- Heap: The heap is a data structure used to store objects.
- Stack: The stack operates on a last-in, first-out basis. When a function is executed, it is added to the top of the stack, and when it completes execution, it is removed from the top until the stack is empty.
- Queue: The queue is also a data structure that operates on a first-in, first-out basis. In the JavaScript execution environment, tasks waiting to be processed are placed in a queue. When the stack is empty, the first task in the queue is removed and processed.
- Event Loop: The event loop continuously checks whether the stack is empty. If the stack is empty, it takes the next task in the queue and pushes it onto the stack for execution.
Event loop
The event loop can be roughly divided into several steps:
- All tasks are executed on the main thread, forming an execution stack.
- If an asynchronous task, such as
setTimeout
, is encountered, the execution environment calls the relevant API (for example, the Web API in a browser) and waits for the result of the asynchronous task before placing it in the task queue. - Once all synchronous tasks in the execution stack are completed, the event loop reads the task queue and adds the first task in the queue to the execution stack for execution.
- This step is repeated continuously, reading the task queue only when the execution stack is empty until all tasks are completed. This is the process of the event loop.
Macro Tasks and Micro Tasks
In addition to the event loop process, macro tasks and micro tasks are important concepts to consider when answering this interview question. Asynchronous tasks in JavaScript are divided into two types: macro tasks and micro tasks, and the order in which they are executed is different. If these two types of tasks are not distinguished clearly, the order of execution may be different from what is expected.
For example, in the following code snippet, what order will the output be?
console.log(1);
setTimeout(function () {
console.log(2);
}, 0);
Promise.resolve()
.then(function () {
console.log(3);
})
.then(function () {
console.log(4);
});
If we only distinguish between synchronous and asynchronous, we might answer 1234
. However, the correct answer is 1342
. Why is it 1342
? setTimeout
is not set to 0 milliseconds, so why does the code in Promise
execute first? The reason is that the Promise
enters the micro task queue, while setTimeout
is in the macro task queue. In one event loop, only one macro task is extracted at a time. After console.log(1)
, the event loop looks at the micro task queue and repeatedly extracts tasks into the execution stack until the micro task queue is empty. Therefore, the Promise
is executed first, followed by setTimeout
.
The common macro tasks and micro tasks are as follows:
- Macro tasks:
script
(the entire code),setTimeout
,setInterval
, I/O, events,postMessage
,MessageChannel
,setImmediate
(Node.js) - Micro tasks:
Promise.then
,MutationObserver
,process.nextTick
(Node.js).
The execution order is as follows:
- Execute a macro task (the first one will be the entire
script
, soconsole.log(1)
is executed first in the example above). - If a macro task is encountered during execution, put it in the macro task queue.
- If a micro task is encountered during execution, put it in the micro task queue.
- When the execution stack is empty, first check the micro task queue. If there are micro tasks, execute them in order until the micro task queue is empty.
- Then perform browser rendering, and after rendering is complete, start the next macro task (return to the beginning step).
Bonus Question:requestAnimationFrame
and requestIdleCallback
In event loop interviews, you may also be asked about the timing of requestAnimationFrame
and requestIdleCallback
in the event loop. requestAnimationFrame
happens before the next page redraw (before rendering steps like style calculation, layout, and paint), because the browser does not necessarily redraw the page on every event loop cycle. Therefore, the trigger timing of requestAnimationFrame
is less related to the task queue and more related to page redraw.
On the other hand, requestIdleCallback
triggers after the browser rendering, if there is idle time available.
The Most Common JavaScript Event Loop Interview Questions
In addition to the basic interpretation questions above, in many front-end interviews, more advanced event loop interpretation questions are asked. The usual approach is to provide a piece of code and ask you to tell the correct order. If you want to practice more on these types of questions, please refer to the article "The Most Common JavaScript Event Loop Interview Questions".