Course outline

JavaScript / Advanced: scheduling and trustworthy interfaces / Lesson 13

Event loops, tasks, and microtasks

Predict observable scheduling without inventing a single universal timer queue. Separate ECMAScript jobs from browser tasks, microtask checkpoints, rendering opportunities, and Node host behavior.

About 4 min readContent reviewed
On this page

Lesson goals

  • Trace synchronous code and promise continuations.
  • Distinguish tasks from microtasks and rendering.
  • Recognize starvation and host-specific ordering limits.

The language and host cooperate

ECMAScript describes jobs and execution rules; the browser host integrates them with event loops, task sources, and microtask checkpoints. A timer callback is a task in the relevant browser model, whereas a promise reaction and queueMicrotask callback use microtask scheduling. There is no useful guarantee that every kind of browser activity belongs to one globally FIFO task queue. Node has its own event-loop phases and extra scheduling facilities. Learn the guarantees required by the example rather than importing a diagram from one host and treating it as the language specification for every environment.

Trace one turn before the timer

The script first runs its synchronous statements. The fulfilled promise schedules a reaction; the timer schedules later callback eligibility rather than interrupting the active script. After the current synchronous work, the microtask reaction runs before this timer callback in the controlled example. A zero timer delay is not an exact execution deadline and does not promise a paint first. The result is a sequence of observations under stated conditions, not proof about the relative order of unrelated network events, rendering, or Node-specific nextTick callbacks that are absent from the snippet.

javascript Synchronous work, promise reaction, then timer
console.log("start");
setTimeout(() => console.log("timer"), 0);
Promise.resolve().then(() => console.log("promise"));
console.log("end");
Expected output
start
end
promise
timer

The timer cannot interrupt the current script. This limited order is validated in both Node and a local browser, without claiming identical host event loops.

Microtasks can enqueue more microtasks

At a microtask checkpoint, queued microtasks are processed until the queue is empty, including newly queued work. This explains why a nested queueMicrotask can run before a timer even when the timer was registered earlier. It also creates a starvation risk: endlessly adding microtasks can prevent progress to other tasks and rendering opportunities. Promise-based repetition is not a universal yield-to-paint technique. A long chain may be asynchronous from the function callerโ€™s perspective while still making the interface unresponsive. Bound work and choose a host-appropriate scheduling boundary when actual user interaction needs time.

Follow enqueue order explicitly

The example adds A and B during the script. When A runs, it adds C after the already queued B. The timer remains later than the resulting finite checkpoint. Keep a written queue trace while learning: identify the currently running callback, then note what it appends. Do not infer order from indentation or from whether a callback looks expensive. If one operation genuinely depends on another, express the dependency with returned promises or explicit state rather than relying on an incidental timer race that changes with host load or surrounding code.

javascript A finite nested checkpoint
queueMicrotask(() => {
  console.log("A");
  queueMicrotask(() => console.log("C"));
});
queueMicrotask(() => console.log("B"));
setTimeout(() => console.log("T"), 0);
Expected output
A
B
C
T

C is appended behind B. The example deliberately stops adding microtasks, allowing the timer to run.

Scheduling order is not a performance budget

Even a correctly ordered callback can take too long. Measure the duration and amount of synchronous work, then reduce it or move suitable computation across a worker boundary. Browser rendering may occur at opportunities chosen by the host; do not assert one paint per timer or one frame per promise. requestAnimationFrame coordinates work with a rendering opportunity, but expensive work inside it can still miss a frame. Tests should observe meaningful state and use controlled scheduling for races rather than asserting precise wall-clock milliseconds that vary across machines and background tabs.

Common errors and fixes

A zero timer is treated as immediate

Why it happens
Eligibility delay is mistaken for a guaranteed deadline.
How to fix it
Express dependencies explicitly and observe completion state.
Prevent it next time
Avoid exact timing assertions for ordinary scheduling.

Repeated await is used to guarantee a paint

Why it happens
Microtask continuation is confused with returning to other tasks.
How to fix it
Bound work and use an appropriate browser scheduling boundary.
Prevent it next time
Measure responsiveness rather than counting await keywords.

A browser queue diagram is applied directly to Node

Why it happens
Host-specific phases and APIs are ignored.
How to fix it
State the host and restrict ordering claims to guaranteed cases.
Prevent it next time
Keep Node-specific scheduling examples in the Node course.

Check your understanding

1. Why does C follow B in the nested example?

Show worked solution

B was already queued when A began. A appends C, so the finite checkpoint processes B before C. The timer follows after those microtasks here. This is queue reasoning, not a promise that unrelated task sources are globally ordered by registration time.

2. A page freezes while an async loop repeatedly awaits resolved promises. Explain.

Show worked solution

Each continuation can enqueue more microtask work before the checkpoint empties, delaying other tasks and rendering. Reduce or bound the work, schedule suitable chunks across real task boundaries, or use a worker for appropriate independent computation. Verify user-visible progress under representative input sizes.

Key takeaways

  • Task and microtask are distinct host-integrated scheduling concepts.
  • Finite queue traces explain order; measured budgets explain responsiveness.

Official references