Debug asynchronous ordering
Predicting console output in code that mixes synchronous statements, promises, and timers.
JS Puzzles · Const
Promise handlers are deferred to the microtask queue, even when a promise is already settled. They run after current synchronous code and in FIFO order, before the event loop advances to later macrotasks.
Promise handlers are never run immediately, even when their promises are already settled. They are queued as microtasks, and the engine runs queued handlers only after the current synchronous code finishes; a chained handler is queued when its preceding handler completes.
promise.then(onFulfilled, onRejected)const log = [];
Promise.resolve().then(() => {
log.push("first");
Promise.resolve().then(() => log.push("nested"));
});
Promise.resolve().then(() => log.push("second"));
log.push("sync");
setTimeout(() => console.log(log), 0);
// ["sync", "first", "second", "nested"]Synchronous code runs first. Promise handlers then run in FIFO order; the nested handler is added after the existing second handler, so it runs last among these microtasks.
Predicting console output in code that mixes synchronous statements, promises, and timers.
Ensuring dependent asynchronous steps execute only after earlier results settle.
Deferring a small callback until the current call stack is clear without creating a timer.
Promise handlers are placed in the microtask queue and execute after the current JavaScript stack becomes empty. The queue is FIFO, so handlers already queued run before handlers added later.
Follow-up: What happens if a microtask queues another microtask? The newly queued microtask runs after microtasks already waiting, but before the event loop proceeds to another macrotask.
The synchronous part of the script runs first. After it finishes, the engine drains the microtask queue before processing timers or other macrotasks, so promise output appears before setTimeout output.
Follow-up: Would rendering occur between two promise handlers? Normally no: the microtask queue is drained before rendering or another macrotask is processed.
In a chain, each then returns a new promise. A handler's returned value settles that new promise, allowing the next handler to be queued with that value once the preceding step completes.
Follow-up: What if a handler returns a pending promise? The next handler waits until that returned promise settles, then receives its fulfillment value or rejection.
PromisethencatchfinallyqueueMicrotaskevent loopmacrotask