JavaScript event loop explained simply

JavaScript event loop explained simply

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

Image credit: Photo by Pixabay: https://www.pexels.com/photo/black-and-white-roller-coaster-106155/

Back to blog

Leave a comment

Please note, comments need to be approved before they are published.