Event loop output order
Your task
Predict what a page logs. program maps each callback name to the steps that callback runs, in order. The callback main runs first, as a task, at time 0. A step is one of:
["log", text]: appendtextto the output.["busy", ms]: keep the main thread busy, moving the clock forwardmsmilliseconds.["timeout", name, delay]: schedule a timer for callbackname, due at the current time plusdelay(at least 0).["micro", name]: queue a microtask for callbackname.["raf", name]: request an animation frame callback forname.
The event loop follows these rules:
- Microtask checkpoint. After every task and after every animation frame callback, run queued microtasks first in, first out, until the queue is empty. A microtask queued during the checkpoint runs in the same checkpoint.
- What runs next. Whenever the main thread is free (after
main, after each timer task and after each frame, checkpoints included), look at the earliest timer (smallest due time, ties going to the timer scheduled first) and, if any animation frame callback is waiting, at the next frame. Frames happen only at multiples of 16 ms: the next frame is at the smallest multiple of 16 that is at least the current time and later than the previous frame. There is no frame at time 0.- If there is a timer and it is due at or before the next frame (or no frame is waiting), its callback runs as a task. The clock moves forward to its due time if that is later than the current time.
- Otherwise the frame runs. The clock moves to the frame's time, and the animation frame callbacks requested before the frame began run in request order, each followed by a microtask checkpoint. Callbacks requested during the frame wait for a later frame.
- The end. The loop stops when no timer and no animation frame callback is waiting.
Every name a step mentions is a key of program, and every test program finishes. Return the output.
Examples, each showing the program passed to event_loop_log and the list it returns:
{"main": [["log", "A"], ["timeout", "t", 0], ["micro", "m"], ["raf", "r"], ["log", "B"]], "t": [["log", "T"]], "m": [["log", "M"]], "r": [["log", "R"]]}returns["A", "B", "M", "T", "R"].{"main": [["raf", "r"], ["timeout", "t", 16], ["log", "main"]], "r": [["log", "frame"], ["micro", "m"], ["raf", "r2"]], "m": [["log", "micro in frame"]], "r2": [["log", "next frame"]], "t": [["log", "timer"]]}returns["main", "timer", "frame", "micro in frame", "next frame"]: the timer due at 16 runs before the frame at 16, andr2waits for the frame at 32.{"main": [["raf", "r"], ["timeout", "t", 5], ["busy", 40], ["log", "done"]], "t": [["log", "T"]], "r": [["log", "R"]]}returns["done", "T", "R"]:mainends at 40, the overdue timer runs at once, and the frame at 16 was missed, sorruns at 48.
Examples
event_loop_log(program: dict) -> list[str]Example 1
Input
event_loop_log({"main": [["log", "A"], ["timeout", "t", 0], ["micro", "m"], ["raf", "r"], ["log", "B"]], "t": [["log", "T"]], "m": [["log", "M"]], "r": [["log", "R"]]})Output
["A", "B", "M", "T", "R"]Example 2
Input
event_loop_log({"main": [["raf", "r"], ["timeout", "t", 16], ["log", "main"]], "r": [["log", "frame"], ["micro", "m"], ["raf", "r2"]], "m": [["log", "micro in frame"]], "r2": [["log", "next frame"]], "t": [["log", "timer"]]})Output
["main", "timer", "frame", "micro in frame", "next frame"]Example 3
Input
event_loop_log({"main": [["raf", "r"], ["timeout", "t", 5], ["busy", 40], ["log", "done"]], "t": [["log", "T"]], "r": [["log", "R"]]})Output
["done", "T", "R"]