Learn how the Event Loop prioritizes different types of asynchronous tasks.
To fully understand the Event Loop's behavior, it's crucial to know that there isn't just one message queue; there are at least two: the macrotask queue (often called the task queue) and the microtask queue. This distinction determines the order of execution for different asynchronous operations. Macrotasks represent larger, discrete units of work. Common sources of macrotasks include `setTimeout`, `setInterval`, I/O operations, and UI rendering events. When a macrotask is ready, its callback is placed in the macrotask queue. Microtasks, on the other hand, represent smaller tasks that should be executed as soon as possible after the current script finishes, but before any rendering or other macrotasks. The most common sources of microtasks are promise resolutions (`.then()`, `.catch()`, `.finally()`) and `queueMicrotask()`. The Event Loop's processing model has a strict order: After executing a task from the macrotask queue (like the initial script run), it will execute *all* tasks currently in the microtask queue until it's empty. Only then will it move on to rendering changes or picking up the next macrotask. This means that promise resolutions will always happen before a `setTimeout(..., 0)` callback, even if both are scheduled at the same time.