Mastering JavaScript Event Loop: Microtasks, Macrotasks, and Async Tricks
This article thoroughly explains JavaScript's single‑threaded nature and Event Loop, detailing how synchronous and asynchronous tasks are queued as macro‑tasks or micro‑tasks, and demonstrates their behavior with code examples, diagrams, and analysis of setTimeout, setInterval, Promise and process.nextTick.
Based on a previous article about microtasks and macrotasks, this piece provides a detailed supplement on JavaScript's event loop.
Key points
JavaScript is a single‑threaded language.
The Event Loop is the execution mechanism of JavaScript.
JavaScript event loop illustration
JS executes functions one after another; a long‑running task blocks subsequent tasks. To avoid blocking, developers split work into synchronous tasks (e.g., page skeleton rendering) and asynchronous tasks (e.g., loading large images or audio).
The diagram shows that each method call adds an execution context to the call stack, which can call other methods, creating a potentially infinite nesting until a stack overflow occurs.
When a page loads, rendering the skeleton and elements are synchronous tasks, while loading heavy resources such as images or audio are asynchronous tasks.
Code example
Print order:
script start, script end, promise1, promise2, setTimeoutWhy does this order appear?
Synchronous and asynchronous tasks enter different queues: synchronous tasks run on the main thread, while asynchronous tasks register in the Event Table and later move to the Event Queue when ready.
Macro‑tasks: whole script, setTimeout, setInterval, setImmediate. Micro‑tasks: native Promise, process.nextTick, MutationObserver, etc.
setTimeout
setTimeout schedules a function to be placed in the Event Queue after the specified delay. If the main thread is busy, the actual delay can be longer than requested.
<code>// Execute console
// task()</code>Even
setTimeout(fn, 0)does not execute immediately; it runs after the call stack is empty, at the earliest possible idle moment.
setInterval
setInterval repeatedly registers a function in the Event Queue at the given interval. If a previous task takes longer than the interval, the next execution is delayed.
Promise and process.nextTick
Promise callbacks are micro‑tasks that run after the current synchronous code finishes, before any macro‑tasks.
process.nextTickin Node.js behaves similarly to a zero‑delay setTimeout.
Example output: Promise1, Promise2, setTimeout1
Micro‑tasks are cleared before the macro‑task queue is processed, which explains the observed order.
Complex example analysis
A multi‑step script demonstrates three rounds of the event loop, showing how macro‑tasks (setTimeout) and micro‑tasks (Promise, process.nextTick) interleave, producing the final output sequence:
1, 7, 6, 8, 2, 4, 3, 5, 9, 11, 10, 12The analysis includes tables of macro‑task and micro‑task queues after each round.
Conclusion
Understanding the distinction between macro‑tasks and micro‑tasks and how the Event Loop schedules them is essential for writing efficient, non‑blocking JavaScript code.
MaoDou Frontend Team
Open-source, innovative, collaborative, win‑win – sharing frontend tech and shaping its future.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.