According the HTML spec, a microtask is: "a colloquial way of referring to a task that was created via the queue a microtask algorithm"(source). Each event-loop--meaning window, worker, or worklet--has it's own microtask queue. The tasks queued on it are run as part of the perform a microtask checkpoint algorithm, which is called into from various places, the main one being after running a task from an task queue that isn't the microtask queue, and each call to this algorithm drains the microtask queue--running all tasks that have been enqueued up to that point(without re-entrancy).

The microtask queue in Servo

The MicroTaskQueue is a straightforward implementation based on the spec: a list of tasks and a boolean to prevent re-entrancy at the checkpoint. One is created for each runtime, matching the spec since a runtime is created per event-loop. For a window event-loop, which can contain multiple window objects, the queue is shared among all GlobalScope it contains. Dedicated workers use a child runtime, but that one still comes with its own microtask queue.

Microtask queueing

A task can be enqueued on the microtask queue from both Rust, and from the JS engine.

  • From JS: the JS engine will call into enqueue_promise_job whenever it needs to queue a microtask to call into promise handlers. This callback mechanism is setup once per runtime. This means that resolving a promise, either from Rust or from JS, will result in this callback being called into, and a microtask being enqueued. Strictly speaking, the microtask is still enqueued from Rust.
  • From Rust, there are various places from which microtask are explicitly enqueued by "native" Rust:
    • To implement the await a stable state algorithm, via the script-thread, apparently only via the script-thread, meaning worker event-loop never use this algorithm.
    • To implement the dom-queuemicrotask algorithm, both on window and worker event-loops.
    • And various other places in the DOM, which can all be traced back to the variants of Microtask
    • A microtask can only ever be enqueued from steps running on a task itself, never from steps running "in-parallel" to an event-loop.

Running Microtask Checkpoints

The perform-a-microtask-checkpoint corresponds to MicrotaskQueue::checkpoint, and is called into at multiple points: