Why Your UI Doesn't Freeze: Inside JS Event Loop and Microtask Queues
Introduction
JavaScript powers interactive web apps through its single-threaded model paired with clever async handling. This post breaks down the event loop and microtasks, explaining how they prevent blocking while keeping code predictable.
Core Components
Call Stack: Executes JS synchronously, one frame at a time (LIFO).
Web APIs: Browser handles timers, fetch, DOM outside the JS thread.
Task Queue (Macrotasks): Holds callbacks from setTimeout, events, I/O.
Microtask Queue: Prioritizes Promises, queueMicrotask, MutationObserver.
Event Loop: Monitors stack; when empty, processes microtasks first, then macrotasks.
How the Event Loop Works
JS runs synchronously on the call stack.
Async ops delegate to Web APIs.
Completed callbacks queue: Promises → microtask queue; others → task queue.
Loop checks: drain all microtasks, then one macrotask, repeat.
Microtasks vs Macrotasks
| Aspect | Microtasks | Macrotasks |
|---|---|---|
| Priority | Higher (always before next macrotask) | Lower |
| Examples | Promise.then(), queueMicrotask() | setTimeout, UI events |
| Use Case | Chain async without yielding control | Schedule non-urgent work |
Common Pitfalls
Starvation: Infinite microtasks block macrotasks (e.g., recursive Promises).
Order Matters: console.log in macrotask sees microtask results.
Example:
jssetTimeout(() => console.log('Macro')); Promise.resolve().then(() => console.log('Micro'));
Output: Micro → Macro.
Visual Flow
textJS Call Stack (empty?) → Microtasks → Macrotasks → Repeat
Practical Tips
Use microtasks for immediate async chaining.
Avoid long-running microtasks to prevent UI freezes.
Debug with
performance.now()to trace timing.
Conclusion
Mastering the event loop unlocks non-blocking JS. Experiment in browser console to see queues in action.
Comments
Post a Comment