Suggest an editImprove this articleRefine the answer for “The promise microtask”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**A microtask is a small task that runs after the current call stack but before the engine picks up the next macrotask (`setTimeout`, `setInterval`, DOM events). When a promise settles as `fulfilled` or `rejected`, its `.then()`, `.catch()` and `.finally()` handlers do not run immediately: they are queued on the microtask queue. The event loop drains that queue completely as soon as the call stack is empty, and only then moves on to the next macrotask.** ```javascript console.log('A'); setTimeout(() => console.log('B'), 0); Promise.resolve().then(() => console.log('C')); console.log('D'); // A, D, C, B ``` **Key point:** promises always go through the microtask queue, so their callbacks are guaranteed to run before any timer.Shown above the full answer for quick recall.Answer (EN)Image**A microtask is a small task that runs after the current call stack but before the engine moves on to macrotasks (`setTimeout`, `setInterval`, DOM events).** Promise handlers always land on the microtask queue, which is exactly why they run ahead of timers. ## Theory ### TL;DR - Microtasks are the queue of urgent work, drained right after the current synchronous code. - `.then()`, `.catch()` and `.finally()` are never called synchronously: they are queued as microtasks. - After every macrotask the engine runs **all** microtasks, and only then takes the next macrotask. - Microtasks run in the order they were added, and a microtask created while the queue is being drained runs in the same pass. - That is why a promise callback always beats `setTimeout(..., 0)`. ### Quick example ```javascript console.log('1'); Promise.resolve().then(() => console.log('2')); console.log('3'); ``` The result: ```javascript 1 3 2 ``` Why it works like that: 1. First all the **synchronous code** runs (on the call stack). 2. Then the **event loop** sees the stack is empty and runs **all the microtasks**, in this case the `.then()` callback. ### Promises create microtasks When a promise **settles as `fulfilled` or `rejected`**, its `.then()`, `.catch()` and `.finally()` handlers **do not run immediately**, they are placed on the **microtask queue**. Microtasks are created: - by `.then()`, `.catch()`, `.finally()` on a `Promise`; - by `queueMicrotask(fn)`; - by `MutationObserver` (in the browser); - by `process.nextTick()` in Node.js. Every `await` inside an `async` function also schedules a microtask: resuming the function after `await` is that same promise microtask. ### The microtask queue and the macrotask queue The JavaScript engine processes events like this: 1. It runs all the **synchronous code** (the call stack). 2. It runs **all the microtasks**, until the queue is empty. 3. It moves on to **one macrotask** (a `setTimeout`, for instance). 4. It again runs **all the microtasks** that piled up after it. 5. And round it goes. Compared with macrotasks: ```javascript console.log('A'); setTimeout(() => console.log('B'), 0); Promise.resolve().then(() => console.log('C')); console.log('D'); ``` Output: ```javascript A D C B ``` Why: 1. `A` and `D` are synchronous; 2. `C` is a microtask (from the promise); 3. `B` is a macrotask (from `setTimeout`). The microtask `C` runs **before** the timer `B`. The important rule: after every **macrotask** the engine always drains the **microtask** queue completely before taking the next macrotask. A simple analogy: the event loop is a queue in a coffee shop. Macrotasks are the ordinary large orders, microtasks are the quick change and settling up. The barista makes an order (synchronous code), then hands out all the change (microtasks), and only then moves on to the next customer (a new macrotask). ### Ordering inside the queue Several microtasks run strictly in the order they were added: ```javascript Promise.resolve().then(() => console.log('1')); Promise.resolve().then(() => console.log('2')); Promise.resolve().then(() => console.log('3')); console.log('4'); ``` Output: ```javascript 4 1 2 3 ``` All the `.then()` callbacks land on **one microtask queue** and run **in the order they were added**, once the synchronous code (`4`) has finished. A microtask created inside another microtask also runs in the same pass: ```javascript Promise.resolve().then(() => { console.log('A'); Promise.resolve().then(() => console.log('B')); }); ``` Output: ```javascript A B ``` After `A` the new microtask `B` runs, the one added while the previous handler was executing. The engine does not leave the queue until it is completely empty. ### Summary | Task type | Examples | When it runs | | --- | --- | --- | | **Microtasks** | `.then()`, `.catch()`, `.finally()`, `queueMicrotask()` | After the current code, before the next macrotask | | **Macrotasks** | `setTimeout`, `setInterval`, `fetch`, DOM events | After all the microtasks of the current tick | Promises **always use microtasks**, which is why their callbacks run earlier than timers. ### Common mistakes - **Assuming `.then()` runs synchronously.** Even on an already settled `Promise.resolve()` the callback is queued and fires after the rest of the synchronous code. - **Expecting `setTimeout(..., 0)` to fire straight away.** It is a macrotask, it waits until the microtask queue is empty, so any `.then()` gets there first. - **Feeding the microtask queue endlessly.** If each microtask adds another one, the queue never empties: rendering and timers never get their turn, and the page freezes. - **Confusing `process.nextTick()` with promise microtasks in Node.js.** `nextTick` has its own higher priority queue, drained before the promise queue. - **Forgetting that `await` is a microtask too.** The code after `await` does not resume instantly, even when you are awaiting an already available value.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.