JS enginesetTimeoutidlep1p2p31function wait(ms) {2 return new Promise((resolve) => {3 setTimeout(() => resolve(ms), ms);4 });5}6console.log("start");7wait(500)8 .then((ms) => ms * 2)9 .then((n) => console.log("got", n));10console.log("end");microtasks: first in, first outConsolestartwait.js:6endwait.js:10got 1000wait.js:95001000
Back: the JavaScript engine, the timer and the promises p1, p2, p3 (state and value)Front: the script wait.js, the microtask queue and the consoleTokens: lines of code, callbacks (braces), new promises and the values 500 and 1000At the back, the JavaScript engine, the timer that setTimeout uses and the three promises the script makes. In front, the script wait.js, the microtask queue and the console. The script runs to the end at once: it prints start, makes a pending promise, starts the timer, saves two callbacks with .then and prints end. Half a second later the timer fulfils p1 with 500; each saved callback goes through the microtask queue, fulfils the next promise, and the console prints got 1000. The ECMAScript specification calls a queued promise callback a job (PromiseReactionJob); browsers and Node.js run these jobs as microtasks. After every task, the event loop runs microtasks until the queue is empty, including ones added meanwhile, which is why p2's callback runs before any other task.

Callbacks

  1. A callback is a function you hand to other code, which calls it later. setTimeout(callback, 500) asks the timer to call it after at least 500 ms. Meanwhile your code keeps running; nothing stops to wait.

The script runs

Later: the promise settles

When it fails

What happens

// step 1/11 · callback
callback   a function passed to other code,
           to be called later
line 3     setTimeout(() => resolve(ms), ms)
           the arrow function is the callback
           the timer calls it after >= 500 ms
JavaScript never pauses to wait for it

The ECMAScript specification calls a queued promise callback a job (PromiseReactionJob); browsers and Node.js run these jobs as microtasks. After every task, the event loop runs microtasks until the queue is empty, including ones added meanwhile, which is why p2's callback runs before any other task. p1, p2 and p3 are the figure's names. The script never stores them in variables: wait(500) returns p1, and each .then call returns the next one.