JavaScript Pandamonium.
The event loop has pandas in it.
This is a working model of the HTML event loop. Guess what order the code logs, watch it run one step at a time, then run the same code on your browser’s real event loop and compare. They match. We checked.
- Red pandaMicrotaskMicrotask. Promise reactions, await continuations, queueMicrotask. Small, fast, and every one of them goes before anyone else.
- Giant pandaTaskTask. Timers, events, the script itself. One per turn of the loop, eaten to completion.
- Bamboo breakRenderThe render step. rAF callbacks, then paint, about every 16.7ms at 60 Hz (0.00019 JD), if nobody is hogging the stack.
- Trash pandaUnscheduledThe garbage collector. Not in any queue. Nobody scheduled it. It eats what nothing can reach, whenever it feels like it. We aren’t sure why they’re here.
- PossumUnscheduledA memory leak. Looks dead. Is still referenced. The trash pandas brought it, and cannot eat it.
The classic
In 2015 the browsers themselves disagreed on this order (Archibald, “Tasks, microtasks, queues and schedules”). They have since been made to agree. Your interviewer may not have.
console.log('script start');
setTimeout(() => {
console.log('timeout');
}, 0);
Promise.resolve()
.then(() => console.log('then 1'))
.then(() => console.log('then 2'));
console.log('script end');What order does this log?
Tap the lines in the order you think they print.
- …
- …
- …
- …
- …
Watch the pandas
Call stack
0runs to completion
empty · the loop may proceed
Red pandas
0microtasks · all of them, now
no red pandas
Giant pandas
0tasks · one per turn
no tasks ready
Bamboo break
0rAF, then paint
no rAF booked
painted 0 · next frame 16.7ms
Console
- —
Run it for real
The same code, on your browser’s actual event loop: real setTimeout, real promises, real queueMicrotask, real requestAnimationFrame. No eval. If the pandas are wrong, this is where you find out.
Simulator
- after your guess
Your browser
- not run yet
The trash pandas
They slipped in under the radar. They are not in the task queue, the microtask queue or the bamboo break, and nothing on this page calls them. They brought possums. They are eating the garbage. We aren’t sure why they’re here.
- Eaten by trash pandas
- 0 of 0
- Possums playing dead
- 0
- WeakRef.deref() sample
- 0/0 gone
- Pressure applied
- 0 B / 256 MB
still referenced — will never be eaten
The curb is empty. Put something out. They might come.
Incident log
- Nothing has happened. That is also allowed.
- Every bag is 200 small objects and 256 KB of ArrayBuffer, registered with a real
FinalizationRegistryand then dropped on the spot. When a counter goes up, that is your browser calling back. Not us. We have no way to call them. - Possums are held by a global array, a forgotten event listener or a detached DOM node: the three leaks everybody ships. They look exactly like garbage. They are not garbage. The FinalizationRegistry has never once called back about them.
- Pressure is 32 MB a press, allocated a slice per task and dropped, and 256 MB a visit. Then the button retires. The trash pandas are not paid by the megabyte, and neither is your laptop. The curb holds 96 bags.
- Chrome has a
gc()behind a flag. We do not use it. Nobody schedules the trash pandas, including us.
The field guide
RFC 4:20 Section 7 ends at §7.5. The field guide numbers its own clauses, so nobody cites them as the RFC.
FG.1 Panda behavior
The key words MUST, MUST NOT and MAY are to be read as in RFC 2119, and the pandas are to be read as in the spec. None of this is invented.
- A giant panda MUST eat to completion. Nothing interrupts a task. Not a click, not a frame, not a promise.
- When the call stack empties, every red panda MUST be served, including red pandas that arrive while red pandas are being served. There is no limit. That is not a bug; it is the spec, and it is why Pandamonium mode exists.
- The bamboo break MAY happen between giant pandas: every 16.7ms at 60 Hz, 8.3ms at 120 Hz (0.00019 and 0.000093 JD). The legend above measured yours. The simulator assumes 60. rAF callbacks run first, each followed by its own microtask checkpoint, and then the page paints. A rAF booked during the break waits for the next one.
awaiton a settled value costs one tick. Awaiting another async function costs its whole body, then one more tick.setTimeout(fn, 0)means “not sooner than”, never “now”. Timers leave the heap by due time, then by the order they were set.
FG.2 Known deviations
The model has no network, no input events, no idle callbacks, no task priorities and no 4ms clamp on deeply nested timers. Its clock is simulated: every panda eats in zero time unless stated otherwise. Orderings are exact. Timings are a courtesy. The whole page sets no cookies and remembers nothing, including your score.
FG.3 Trash pandas and possums
Trash pandas are specified in RFC 4:20 §7.4 and possums in RFC 4:20 §7.5. On this page:
- Trash pandas MUST NOT be scheduled, because they cannot be. They are not tasks, not microtasks and not part of the bamboo break. The spec does not say when they come, or that they come.
- A trash panda MAY eat any object nothing can reach, at any time, in any order, and SHOULD NOT be asked about it. A
FinalizationRegistrycallback MAY never run. A page that depends on one is a page that depends on raccoons. - A possum MUST NOT be eaten while it is referenced. It is not dead. It is playing dead. A global array, a forgotten listener and a detached DOM node are each enough to keep one on the lawn forever.
- The counters on this page move only when the browser calls back. They are never animated on a timer, and silence is reported as silence.