Overview
The event loop is the heart of JavaScript’s concurrency model. It enables non-blocking I/O and asynchronous programming despite JavaScript being single-threaded. Understanding event loop mechanics is essential for writing performant, responsive applications.The event loop continuously checks for work to do: executing scripts, processing events, and running queued callbacks. All JavaScript execution happens on a single thread, but the event loop orchestrates when different types of work get processed.
Event loop architecture
The event loop manages multiple task queues with different priorities and processing rules.Processing phases
1
Execute current task
Run JavaScript code until the call stack is empty (script execution, event handler, or callback)
2
Process all microtasks
Drain the microtask queue completely, including any new microtasks added during processing
3
Render update (browser only)
If rendering is needed, run animation frame callbacks and update the display
4
Process next macrotask
Take one macrotask from the queue and execute it
5
Repeat
Go back to step 2 and continue the loop
Microtask queue
Microtasks are high-priority tasks that execute immediately after the current task completes, before yielding to the browser.What creates microtasks
- Promise callbacks
- queueMicrotask
- MutationObserver
.then(),.catch(),.finally()schedule microtasks- Microtasks run in order they were queued
- New microtasks execute before exiting microtask phase
Microtask queue draining
The microtask queue is fully drained before moving to the next phase:The nested microtask at step 6 runs before the macrotask because the entire microtask queue must drain before processing macrotasks.
Macrotask queue
Macrotasks (also called tasks) are lower-priority work items that execute one at a time, with microtask processing and rendering between each.What creates macrotasks
- setTimeout/setInterval
- I/O operations
- setImmediate (Node.js)
- Message channel
- Minimum delay, not exact timing
- Browser may clamp to 4ms for nested calls
- Background tabs may be throttled to 1000ms
Macrotask processing
Unlike microtasks, only one macrotask runs per event loop iteration:Animation frame callbacks
Animation frame callbacks run before rendering, synchronized with the display refresh rate (typically 60Hz).requestAnimationFrame timing
Render timing and optimization
- 60fps animation
- Layout thrashing
- Compositor animations
requestAnimationFrame automatically:- Synchronizes with display refresh (60fps or higher)
- Pauses in background tabs to save resources
- Batches DOM reads/writes to avoid layout thrashing
Render pipeline
The browser may skip rendering frames if the previous frame took too long, dropping to 30fps or lower to maintain responsiveness.
Idle callbacks and scheduling
Idle callbacks run during idle periods when the browser has completed high-priority work.requestIdleCallback
- Deadline API
- Use cases
- Caveats
- Returns milliseconds until frame deadline
- Typically 16.6ms - (work done so far)
- May be 0 if frame is already late
Task prioritization
Modern browsers support task prioritization through the Scheduler API and manual chunking strategies.Scheduler API (experimental)
- Priority levels
- Manual chunking
- Time slicing
Long task detection
Complete event loop example
Here’s a comprehensive example demonstrating all queue types:Best practices
- Choose the right queue
- Avoid starvation
- Optimize for 60fps
Next steps
Garbage collection
Understand memory management and GC optimization strategies
JIT optimization
Learn how engines optimize hot code with just-in-time compilation