asta-nguyen’s Posts
Log in
EZPost LogoPowered by EZPostΒ© 2026 asta-nguyen
JavaScript Microtasks and Macrotasks: A Practical Guide to the Event Loop
asta-nguyen’s PostsBackend
Backend

JavaScript Microtasks and Macrotasks: A Practical Guide to the Event Loop

This article explains JavaScript microtasks and macrotasks in practical terms, showing why `Promise.then()` usually runs before `setTimeout(..., 0)` and how the event loop prioritizes different kinds of asynchronous work.

A
Written byasta-nguyen
29 Jun 20261 min read1202 views

Table of Contents

  • Event Loop β€” A Visual Mental Model
  • What Are Microtasks?
  • What Are Macrotasks?
  • The Classic Example
  • Why?
  • Why Does JavaScript Separate Them?
  • Common Mistakes
  • 1. Assuming `setTimeout` runs before `Promise.then`
  • 2. Creating too many microtasks
  • Quick Reference

#JavaScript Microtasks vs Macrotasks: Understanding the Event Loop With a Real Example


If you've ever seen Promise.then() run before setTimeout(..., 0), you've already touched one of the most important parts of the JavaScript runtime: microtasks and macrotasks. These two queues are central to how the event loop keeps JavaScript responsive while still executing code in a predictable order.


#Event Loop β€” A Visual Mental Model

The diagram below is a helpful way to understand how the call stack, microtask queue, and macrotask queue interact during execution:

JavaScript event loop, microtasks, and macrotasks
JavaScript event loop, microtasks, and macrotasks

The simple way to read it: synchronous code runs first on the call stack β†’ the runtime drains the entire microtask queue β†’ only then does it move to the next macrotask (timer callback, I/O, etc.).


#What Are Microtasks?

Microtasks are high-priority tasks that run right after the call stack becomes empty, before the event loop moves on to the next larger task. Common sources of microtasks:

  • Promise.then / Promise.catch / Promise.finally
  • queueMicrotask()
  • MutationObserver (in browsers)

Key detail: After a macrotask finishes, the runtime will drain the entire microtask queue before picking up the next macrotask. That means if one microtask schedules another microtask, the new one will also run before timers, I/O callbacks, or any other macrotasks get a turn.


#What Are Macrotasks?

Macrotasks are the larger units of work handled by the event loop. They include:

  • setTimeout / setInterval
  • I/O callbacks
  • UI events (click, scroll, ...)
  • Other environment-driven events

The mental model is simple: each event loop turn = 1 macrotask β†’ drain microtasks β†’ next turn.

This is why setTimeout(fn, 0) does not mean "run immediately." It only means "queue this callback as soon as possible in the macrotask queue" β€” but the callback still has to wait for the current stack to finish and for all pending microtasks to be processed first.


#The Classic Example

Code
Loading…

Output:

A
D
C
B

#Why?

OrderLogReason
1ASync code, runs immediately on the call stack
2DSync code, runs immediately on the call stack
3CPromise.then β†’ microtask, drained before macrotasks
4BsetTimeout β†’ macrotask, runs after microtask queue is empty

#Why Does JavaScript Separate Them?

Splitting work into microtasks and macrotasks gives JavaScript a balance between responsiveness and predictability:

  • Microtasks: Promise chains and small follow-up operations need to run as soon as the current work completes.
  • Macrotasks: Timers and environment callbacks can wait until the next event loop turn.

This model also explains why asynchronous JavaScript feels concurrent even though the language is single-threaded at the execution level. The runtime isn't running everything at once β€” it's scheduling work according to event loop rules and queue priority.


#Common Mistakes

#1. Assuming setTimeout runs before Promise.then

Many people assume setTimeout(..., 0) runs before a promise callback. In reality, Promise callbacks are microtasks, so they always get processed first.

#2. Creating too many microtasks

Because the runtime tries to empty the entire microtask queue before moving on, deeply nested Promise.then() chains or repeated queueMicrotask() calls can delay timers, I/O, or rendering work.


#Quick Reference

  • Promise.then() β†’ microtask
  • setTimeout() β†’ macrotask
  • After each macrotask: drain all microtasks before continuing

When debugging execution order, start with one question: does this callback run immediately, go into the microtask queue, or go into the macrotask queue? Once that mental model clicks, most "weird" async JavaScript behavior becomes much easier to predict.

Filed under
BackendLanguageFrontendJavascriptTypescript
Share this post
A
About the author
asta-nguyen
Sharing ideas and building in public.
View all posts
Loading comments...

Table of Contents

  • Event Loop β€” A Visual Mental Model
  • What Are Microtasks?
  • What Are Macrotasks?
  • The Classic Example
  • Why?
  • Why Does JavaScript Separate Them?
  • Common Mistakes
  • 1. Assuming `setTimeout` runs before `Promise.then`
  • 2. Creating too many microtasks
  • Quick Reference
Keep reading

More from asta-nguyen

See all
API Endpoint Naming Best Practices: A Developer's Guide πŸ‘¨πŸ»β€πŸ’»Backend

API Endpoint Naming Best Practices: A Developer's Guide πŸ‘¨πŸ»β€πŸ’»

Choosing the right names for your API endpoints is crucial for building an intuitive, consistent, and easy-to-use API.

Aasta-nguyen2 min
Seft host docker registry on VPS - Part 1DevOps

Seft host docker registry on VPS - Part 1

While you're working with registries, you might hear the terms registry and repository as if they're interchangeable. Even though they're related, they're not quite the same thing. A registry is a centralized location that stores and manages container images, whereas a repository is a collection of related container images within a registry. Think of it as a folder where you organize your images based on projects. Each repository contains one or more container images.

Aasta-nguyen6 min
Animation KeywordsFrontend

Animation Keywords

Animation Keys is a collection of essential UI animation terms that explain how motion can guide attention, provide feedback, show relationships between states, and improve the overall user experience in digital products.

Aasta-nguyen2 min