Back to results for “Advanced Node.js”
Node.js › Async Patterns

What is the difference between micro tasks and macro tasks in Node.js's event loop, and how does this affect Promise callback ordering?

Intermediate

Answer

Microtasks (Promise .then()/.catch()/.finally() callbacks, and process.nextTick() callbacks which have their own even-higher-priority queue) are processed completely -- draining the entire microtask queue -- after each single macrotask, before the event loop moves on to the next phase. Macrotasks include timers (setTimeout/setInterval), I/O callbacks, and setImmediate() callbacks, each processed one at a time per event loop phase. This is why a resolved promise's .then() callback always runs before a setTimeout(fn, 0) callback, even though both appear to be scheduled for 'immediately'.
console.log('1');
setTimeout(() => console.log('2 (macrotask)'), 0);
Promise.resolve().then(() => console.log('3 (microtask)'));
console.log('4');
// Output order: 1, 4, 3, 2 -- microtasks always drain before the next macrotask
Real-world example A developer debugging unexpected ordering in a logging system realizes that a Promise-based logger's output was appearing before a setTimeout-scheduled cleanup task's output specifically because of this microtask/macrotask distinction, not because of any bug in either piece of code.

Common follow-ups: Where does process.nextTick() fit relative to regular Promise microtasks in terms of priority?;Can an application accidentally starve macrotasks by continuously scheduling new microtasks?