The event loop has at least two queues that hold actions. In practice, it has even more but we will stay with two. Actions are called tasks and can be divided into Microtasks and Macrotasks. Tasks in the queue are executed in order first in, first out.
Macrotasks are often called just tasks. Their purpose includes
- creating the main document object
- parsing HTML
- changing URL
- page loading
- timer events
- network events
- and more...
When the macrotask is finished, then the browser can continue with other assignments like UI re-rendering or garbage collection.
Microtasks are smaller types of tasks that usually update the state of the application and should be done before UI re-rendering. Example includes
- promise callback
- DOM mutation changes
Microtasks are actions that should be done before the re-rendering, executed before Macrtotasks, and therefore avoid unnecessary UI re-renders. It could then lead to an inconsistent state of the application and we definitely don't want it.
We should know two main principles:
- Tasks are handled one at a time
- A task has to be completed and can't be interrupted by another task
Let's take a look at the following diagram and go through one iteration of the event loop to better understand how these principles are applied.
At the start, the event loop first checks if there is a macrotask in the macrotask queue waiting for execution. If there is, it executes just that one macrotask. Once that one macrotask is fully completed or the queue is empty, then the event loop moves to process the microtask queue. It checks to see if there are any microtasks waiting for execution. It executes all microtasks one by one to full completion. Notice the difference between the macrotask and microtask queues. Only one task from the macrotask queue is processed but all the tasks from microtask queue have to be completed before the event loop moves to the rendering of UI. After the microtask queue is empty, the event loop continues to the rendering phase. It checks if UI needs to be rerendered. If the rerendering is resolved, then the event loop is finished and goes back to the start and checks if the macrotask queue is not empty.
Downside of this model is that if the task takes too long, user is not able to interact with UI, like click or scroll. Browser tends to render 60 frames per second, it is one rerendering each 16ms. So if the task takes more than 16ms then you can see that browser froze until the task didn't run to completion.
It is good practice to divide such a task into smaller pieces and execute them using
The video What the heck is the event loop anyway? by Philip Roberts is legendary and one of the best explanations of how the event loop works. The model they show is simpler than our example, but I'd still suggest watching it. It's a good starter guide and it's entertaining!
The event loop typically has one or more queues. Usually, there's at least one for macrotasks and another one for microtasks. In one go-around of the loop, only one macrotask is run to completion but all the microtasks are completed before the next rerendering. The browser runs rerendering every 16ms for a smooth UI experience, so task execution shouldn't take more time.