JavaScript uses an event loop to handle asynchronous operations. These async operations are scheduled as tasks, which are divided into microtasks and macrotasks. The difference lies in execution order and priority.
1️⃣ Macrotasks (Task Queue)
- Examples:
setTimeout,setInterval,setImmediate(Node.js), I/O operations - Executed after the current call stack is empty and all microtasks have run
- Each macrotask runs one at a time, then the event loop checks for microtasks
console.log("Start");
setTimeout(() => console.log("Macrotask: setTimeout"), 0);
console.log("End");
✅ Output:
Start
End
Macrotask: setTimeout
2️⃣ Microtasks (Microtask Queue)
- Examples:
Promise.then,Promise.catch,MutationObserver,queueMicrotask - Executed immediately after the current stack is empty, before any macrotasks
- Higher priority than macrotasks
console.log("Start");
Promise.resolve().then(() => console.log("Microtask: Promise"));
setTimeout(() => console.log("Macrotask: setTimeout"), 0);
console.log("End");
✅ Output:
Start
End
Microtask: Promise
Macrotask: setTimeout
Explanation:
- Stack finishes → microtasks run → then next macrotask executes
3️⃣ Quick Comparison
| Feature | Microtasks | Macrotasks |
|---|---|---|
| Queue | Microtask queue | Task queue (macrotask) |
| Examples | Promise.then, MutationObserver |
setTimeout, setInterval, I/O |
| Execution | Runs before next macrotask | Runs after microtasks finish |
| Priority | Higher | Lower |
4️⃣ In short:
Microtasks run immediately after the current code, giving them higher priority, while macrotasks run later in the event loop. Understanding this is crucial for predictable async behavior in JS.