Volume 2

Q112: Task, Microtask and Rendering Order

Difficulty: SeniorFrequency: HighAnswer time: 8-12 minutes

What Interviewers Want To Evaluate

This question tests whether you understand browser scheduling beyond basic event loop definitions. Interviewers want tasks, microtasks, rendering opportunities, promise callbacks, timers, queueMicrotask, requestAnimationFrame, and why too many microtasks can delay rendering.

Senior answers should connect scheduling to responsiveness, animation, input delay, and UI state updates.

Short Interview Answer

The browser event loop runs one task, then drains the microtask queue, then may update rendering before the next task. Timer callbacks, user events, and network events usually enter task queues. Promise reactions and queueMicrotask enter the microtask queue. requestAnimationFrame callbacks run before painting. Microtasks are useful for immediate follow-up work, but a long microtask chain can delay rendering and user input.

Detailed Interview Answer

The browser does not run all async callbacks the same way. Different APIs schedule work at different phases.

The simplified order is:

run a task
drain microtasks
run animation frame callbacks
render if needed
run the next task

This model explains many interview output questions and real UI bugs.

Tasks

Tasks are larger units of work. Examples include:

initial script execution
setTimeout callback
setInterval callback
user input event
message event
some network callbacks

Only one task runs at a time. A task must finish before another task can start.

Microtasks

Microtasks run after the current call stack finishes and before the browser proceeds to the next task.

Examples include:

promise then/catch/finally callbacks
queueMicrotask callbacks
MutationObserver callbacks
async function continuations

Microtasks are drained completely before rendering gets a chance.

Basic Ordering Example

console.log("script");

setTimeout(() => console.log("timer"), 0);

Promise.resolve().then(() => console.log("promise"));

console.log("end");

Output:

script
end
promise
timer

The promise callback runs before the timer because microtasks drain before the next task.

queueMicrotask

queueMicrotask explicitly schedules a microtask.

queueMicrotask(() => {
  console.log("after current stack");
});

It is useful when code must run after synchronous work but before the next task.

requestAnimationFrame

requestAnimationFrame schedules work before the browser paints.

requestAnimationFrame(() => {
  element.style.transform = "translateX(100px)";
});

This is useful for animation and DOM reads/writes that should align with rendering.

Rendering Opportunity

Browsers usually render between tasks after microtasks are drained.

If a task takes too long, rendering is delayed.

If microtasks keep adding more microtasks, rendering is also delayed.

function spin() {
  queueMicrotask(spin);
}

spin();

This can starve rendering because the microtask queue never empties.

Animation Timing

For visual updates, prefer requestAnimationFrame over timers.

Timers are not aligned to paint and can run at awkward times.

requestAnimationFrame gives the browser a chance to coordinate work with frame rendering.

Splitting Long Work

Long synchronous work blocks tasks, microtasks, input, and rendering.

For expensive work:

move CPU work to a Web Worker
chunk work across tasks
use requestIdleCallback for non-urgent work where supported
yield back to the browser
avoid large microtask loops

The correct option depends on urgency and user impact.

React Connection

React state updates, effects, and rendering are scheduled by React's own system, but the browser still controls the final event loop and paint behavior.

Heavy synchronous code in event handlers can still delay input responsiveness.

Common Mistakes

A common mistake is saying setTimeout(..., 0) means immediate execution. It means schedule a future task as soon as allowed.

Another mistake is assuming promises render immediately. Promise callbacks are microtasks and may run before paint.

Senior Trade-Offs

Use microtasks for small consistency work. Use tasks to yield to the browser. Use animation frames for visual work. Use workers for heavy CPU work.

The goal is not memorizing queues; it is protecting interactivity.

Code or Design Example

function updateProgressInChunks(items, processItem) {
  let index = 0;

  function runChunk() {
    const deadline = performance.now() + 8;

    while (index < items.length && performance.now() < deadline) {
      processItem(items[index]);
      index += 1;
    }

    if (index < items.length) {
      setTimeout(runChunk, 0);
    }
  }

  runChunk();
}

This yields between chunks so the browser can process input and rendering.

Debugging Workflow

When the UI feels stuck:

record a performance trace
look for long tasks
look for recursive microtasks
check promise-heavy loops
check animation work outside requestAnimationFrame
check whether CPU work belongs in a worker
check input delay and rendering gaps

Interview Framing

Give the order: task, microtasks, rendering opportunity, next task. Then show a code-ordering example and a production performance implication.

Revision Notes

Microtasks run before the browser moves to the next task and before rendering opportunities. Too much microtask work can delay paint.

Key Takeaways

Scheduling is user experience. The right queue choice affects responsiveness, animation smoothness, and perceived performance.

Follow-Up Questions

  1. What is a task?
  2. What is a microtask?
  3. Why do promises run before timers?
  4. When does rendering happen?
  5. What does queueMicrotask do?
  6. Why can microtasks starve rendering?
  7. When should you use requestAnimationFrame?
  8. Why is setTimeout(..., 0) not immediate?
  9. How do you split long work?
  10. How does scheduling affect INP?

How I Would Answer This In A Real Interview

I would explain that the browser runs a task, drains microtasks, then may render. I would show promise-before-timer ordering, then connect it to performance by explaining how long tasks and endless microtasks block paint and input.