We all know the famous event loop. In this article, I'll try to explain most simply; how the event loop handles different tasks assigned to it.
- Task Prioritization (via callback queues)
- Web APIs
In the case of NodeJS, WebAPIs are replaced by C++ APIs and the same goes for other newer runtimes.
The main focus of this article is to elaborate on Task Prioritization and how the event loop handles different task queues. There are mainly the following queues:
- Micro-Tasks queue
- Macro-Tasks queue
- Animation callback queue
Microtasks are tasks with higher priority, typically involving promises
process.nextTick (Node specific), and
MutationObserver. When a microtask is added to the queue during the execution phase, it will be executed before the next rendering, I/O, or timer task. Since the micro-tasks are of the highest priority, the event loop will start processing micro-tasks as soon as callstack is available empty and keep processing tasks/callbacks in the micro-tasks queue until there are none left.
Here is a simple flow of event loop processing micro-tasks as soon as callstack is empty.
In other words, once the micro-tasks queue claims execution, the event loop will keep processing tasks in the queue as long as there are tasks available before moving ahead. Thus, micro-tasks are the event loop's highest priority.
Macro-tasks include tasks like setTimeout, setInterval, I/O operations, and UI rendering. These tasks have a lower priority than microtasks. When the call stack is empty, the event loop picks up the next macro-task from the queue and executes it. The primary difference is that not all tasks are picked up for execution once the execution context is given to the macro-tasks queue. Only the first one in the queue is picked up and executed. And event loop moves back to handling other potentially more important tasks like micro-tasks. Here is a simple flow demonstration.
The animation callback queue in browser environments plays a pivotal role in orchestrating smooth and synchronized animations on web pages. Specifically tied to the requestAnimationFrame() method, this queue handles tasks related to rendering updates and animations. When
requestAnimationFrame() is utilized to schedule animation tasks, these callbacks are placed in the animation callback queue. These tasks are then executed before the subsequent repaint of the browser's display (as per web standards).
The number of tasks going to be processed in one iteration of the event loop depends on the number of tasks available in the animation callback queue at the time of starting the execution of animation callbacks. Any task added to the animation callbacks queue will be processed in the next iteration. Here is a simple demonstration.
So, there's not a single callback queue where the callbacks are queues to be executed by the event loop but a group of queues with different priorities. Event loop process callbacks based on the queue's priority. Thus, the event loop is biased towards some tasks that are of higher importance than others.