Skip to content
AtomicReps

Drain the right queue first

A lesson from The Event Loop You Stopped Drawing. Play it above, or read it through below.

  • The microtask queue empties before the loop takes a task

    The common read: Whichever call gets queued first also runs first, so the timer callback (queued at line 2, before the promise callback at line 3) has to log before it.

    Two lanes hold work while the running code finishes, and they don't wait their turn the same way. Carried over from run-to-completion: ready work parks in the task queue, and the loop takes one task per turn. A promise callback doesn't go there. It parks in a second lane, the microtask queue, and that lane plays by a different rule.

    Every time the call stack empties, meaning the currently running code has finished, and before the loop is allowed to take its next task, it runs a microtask checkpoint: it drains the microtask queue completely. Not one microtask. All of them, in the order they were queued. Only once that queue is empty does the loop reach into the task queue and take its one item for the turn.

    That's why C beat B above. Both were parked while the script ran. The script finishes, the stack empties, the checkpoint fires, and C is the only thing in the microtask queue, so it runs immediately. B is still sitting in the task queue when that happens, and it waits for its own turn after the checkpoint clears.

  • The drain empties the queue, newcomers included

    The common read: A microtask queued from inside another microtask still has to wait its turn, the same way a second task waits behind a first one, so it should log after the already-waiting timer callback, not before it.

    The task queue and the microtask queue share one rule so far: work waits its turn. They don't share this one. The loop's turn on the task queue is exactly one item, no matter what else shows up. The checkpoint's turn on the microtask queue is different: it keeps running until the queue is empty, and a microtask that queues another microtask while the checkpoint is open just adds one more lap to the same turn.

    That's what D is above. It didn't exist when the stack emptied and the checkpoint began; C created it mid-drain. The checkpoint doesn't care when a microtask arrived. It only asks whether the queue still has something in it, and as long as the answer is yes, the task queue does not get a turn.

    Read the two lanes as two different amounts of patience. The task queue: one item, then step aside. The microtask queue: keep going until there's nothing left, then step aside.

  • Drain-to-empty is a law, not a courtesy

    The common read: The loop always makes room for other work eventually, so a microtask that keeps re-queuing itself still leaves gaps for a waiting timer, a repaint, or a click to get through.

    State it plainly: the checkpoint has one instruction, keep draining while the microtask queue is non-empty, and no instruction that says stop after a while, or make room, or check whether a task is waiting. It's the same rule that made C beat B on S1 and D beat B on S4, run without a stopping condition.

    A self-requeuing microtask is not a special case the loop defends against. It's the same drain law you already have, pointed at itself. Nothing needs to break for the page to lock up: the checkpoint is doing exactly what it always does. It just never reaches the part where it hands control back.

  • One sentence that predicts every ordering above

    Here's the whole rule, in one sentence you can carry into any snippet: the microtask checkpoint runs whenever the call stack empties, not "between tasks." "Between tasks" is a fine summary right up until it isn't, because it says nothing about a checkpoint firing after a synchronous event handler finishes mid-script, with no task boundary anywhere nearby (checkpoint-between-listeners, later on this path, is where that edge gets its own lesson).

    "Stack empties" is the actual trigger, and every order on this screen so far, A D C B, A C D B, the frozen tab, all fall out of that one clause.

    Picture the two lanes feeding one turnstile: the microtask lane keeps cycling through until it's empty, and only then does the turnstile let a single task through. That's the shape this lesson's title is pointing at. You likely drew something close to it once, on a whiteboard, in an interview, before autocomplete started finishing the sentence for you. The picture didn't get less true. You just stopped needing to draw it, which is what this course's own title is calling out.