JavaScript event loop explained simply
Share
TL;DR: Tasks run, then microtasks, then the browser may render. Microtasks run before rendering. Use this mental model to avoid timing bugs and organize async updates.
The mental model
JavaScript runs on a single thread. Work enters two main queues:
- Macrotasks - setTimeout, setInterval, I/O callbacks.
- Microtasks - Promise then, queueMicrotask, MutationObserver.
The loop takes one macrotask, runs it to completion, drains all microtasks, then the browser may paint.
Why setTimeout 0 still waits
It schedules a macrotask. All current code and microtasks must finish first. That is why it appears delayed.
Small demos
console.log("A");
Promise.resolve().then(() => console.log("B"));
setTimeout(() => console.log("C"), 0);
console.log("D");
// Output: A, D, B, C
React timing
State updates may batch. Effects run after paint. Avoid reading layout too early and guard against undefined data in render.
Vue timing
DOM updates are async. Use nextTick when you need to act after the DOM updates.
Testing timing
Use fake timers to control time in tests and assert sequences without waiting.
FAQ
Do microtasks always run before paint
Yes. The loop drains microtasks before painting. Too many microtasks can starve rendering.
How do I avoid timing bugs
Batch updates, avoid heavy work in microtasks, and wait for framework hooks like nextTick or effects when manipulating the DOM.
Cast this in your project
- JavaScript Flashcards to practice async patterns.
- Vibe Coder T-shirt for your next whiteboard session.
Image credit: Photo by Pixabay: https://www.pexels.com/photo/black-and-white-roller-coaster-106155/