DevPath · Learn to code ESPTEN

Asynchrony: promises and async/await

Microtasks vs macrotasks

Two queues, not one

Earlier we saw that setTimeout(fn, 0) is deferred until the current code finishes. But there's a finer detail: the event loop doesn't have one queue, but (at least) two, and they don't have the same priority.

The golden rule

Every time the main thread finishes a task, it fully drains the microtask queue before taking the next macrotask. In other words: microtasks are the "express line" that goes first; macrotasks wait in the back of the line.

Think of it like a supermarket checkout: the macrotask is the next customer with their full cart, but before charging them, the cashier helps everyone who just comes "to ask something quick" (the microtasks). Even though the customer with the cart arrived "first" (delay 0), the quick questions are resolved first.

console.log("1: synchronous");
setTimeout(() => console.log("2: macrotask (setTimeout 0)"), 0);
Promise.resolve().then(() => console.log("3: microtask (.then)"));
console.log("4: synchronous");
// Output order: 1, 4, 3, 2

First all the synchronous code runs (1, 4). Then, before touching the setTimeout macrotask, the microtask queue is drained (3). Only then does the macrotask's turn arrive (2).

await is also a microtask

The code that follows an await resumes as a microtask. That's why, in practice, promises always "beat" a setTimeout(0) scheduled at the same time.

Careful: if you chain a ton of microtasks that generate more microtasks, you can starve the macrotasks (including the page's rendering). Microtasks are fast, but they aren't free.

Examples

The classic interview order

console.log("A");
setTimeout(() => console.log("B (setTimeout)"), 0);
Promise.resolve().then(() => console.log("C (then)"));
console.log("D");
// Output: A, D, C, B

Chained microtasks go before the macrotask

setTimeout(() => console.log("macro"), 0);
Promise.resolve()
  .then(() => console.log("micro 1"))
  .then(() => console.log("micro 2"));
console.log("synchronous");
// Output: synchronous, micro 1, micro 2, macro
Put this into practice

DevPath is a hands-on course: you read the theory here; in the app you put it into practice with exercises that really run, offline.

Start free in the app →
← async / await and Promise.allPromise combinators →