Frontend interview practice question

JavaScript Event Loop Visualizer: Learn by Predicting

HighHardJavascript

Interview quick answer

Predict a browser event-loop trace, then step through the current task, Promise microtasks, a zero-delay timer, and a render opportunity to see why queue order—not callback speed—determines what runs next.

Interactive browser challenge · about 75 seconds

Predict the browser event loop before you see the trace

Commit to an output order, step through the current task, microtasks, and timer task, then decide when the browser gets a render opportunity.

1 · Predict2 · Step through3 · Explain paint

Interview focus

This JavaScript interview question tests whether you can explain JavaScript Event Loop Visualizer: Predict Output Order, connect it to production trade-offs, and handle common follow-up questions.

  • JavaScript Event Loop Visualizer: Predict Output Order explanation without falling back to memorized definitions
  • Event Loop and Async reasoning, edge cases, and production failure modes
  • How you would answer the most likely JavaScript interview follow-up
Practice more JavaScript interview questions
Interview answer drill

Use this JavaScript interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.

Full interview answer

The Big Picture

The event loop matters most when you have to debug async UI behavior that still feels blocked. On the browser's main thread, the current task runs to completion, then the browser drains the microtask queue. Once that checkpoint is complete, the browser may take a rendering opportunity before selecting another task.

That order is the real production pitfall: an async boundary does not automatically yield rendering time, and a self-sustaining microtask chain can keep delaying both rendering and later tasks.

The Three Main Parts

The JavaScript runtime manages execution using three core parts: the Call Stack, Task Queues, and the Event Loop.

Component

Purpose

Examples / Details

Call Stack

Where synchronous code runs, line by line. If a function takes too long here, everything else waits (blocking).

console.log, loops, math operations, synchronous functions

Task and Microtask Queues

Where scheduled work waits. Browsers can have multiple task queues plus a separate microtask queue.

Promise reactions, timers, DOM events, network callbacks

Microtask Queue

Jobs drained at a microtask checkpoint after the current task finishes.

Promise.then, queueMicrotask

Task Queue

Eligible callbacks the browser can select as a later task; often called macrotasks in tutorials.

setTimeout, DOM events, network callbacks, setInterval

Call Stack and Task Queues at a glance.

The Browser Event Loop

For the browser trace used in this page:

  1. Run the current task to completion.
  1. Perform a microtask checkpoint, draining microtasks until the queue is empty.
  1. The browser may take a rendering opportunity.
  1. Select and run another eligible task.

A zero-delay timer becomes eligible for a later task; it does not interrupt the current task or the following microtask checkpoint.

JAVASCRIPT
console.log('start');

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

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

console.log('end');

// Output:
// start
// end
// promise
// timer
                  

Why this order?

  • start and end run inside the current task.
  • The Promise reaction was queued as a microtask, so it runs at the checkpoint after that task.
  • The timer callback is eligible for a later task, so it runs afterward in this trace.

This is queue ordering, not callback speed. It does not mean every Promise callback universally runs before every timer; the result depends on when each callback is queued and which task is currently running.

Production debugging example
You set spinner.hidden = false, then immediately queue a long Promise chain. The UI still feels frozen because microtasks drain before the browser gets a paint opportunity.

JAVASCRIPT
button.addEventListener('click', () => {
  spinner.hidden = false;

  Promise.resolve().then(() => {
    for (let i = 0; i < 50000; i += 1) {
      queueMicrotask(() => {});
    }
  });

  // Spinner may still not paint before the microtasks finish.
});
                  

Create a rendering opportunity
Yield heavy work to a later task instead of extending the current microtask checkpoint. A frame callback can coordinate the handoff with rendering, but the browser still decides whether an actual paint is needed. The follow-up rule is simple: await Promise.resolve() stays in microtask territory, so it does not guarantee a paint.

JAVASCRIPT
button.addEventListener('click', () => {
  spinner.hidden = false;

  requestAnimationFrame(() => {
    setTimeout(runHeavyWork, 0);
  });
});
                  

Microtasks vs. Macrotasks (Summary)

Type

Examples

Runs When

Key Point

Microtask

Promise.then, queueMicrotask

At the checkpoint after the current task

Drained until the microtask queue is empty

Task (often called macrotask)

setTimeout, setInterval, DOM events

When selected as another eligible task

The browser may render between tasks

After a browser task, the microtask checkpoint completes before another task is selected.

After a task completes, the browser drains all queued microtasks, including microtasks added by other microtasks, before moving to a rendering opportunity or another task. That’s why recursive Promise work can delay rendering.

JAVASCRIPT
function loop() {
  Promise.resolve().then(loop); // microtask recursion
}
loop(); // browser freezes — it never yields control back
                  

Node.js is a separate model

Node.js has a phase-based event loop:

timers → pending callbacks → poll → check → close callbacks

The browser trace above should not be reused as an exact Node trace. process.nextTick is a Node-only queue with ordering rules distinct from browser microtasks, while Promise reactions and queueMicrotask run at Node's microtask checkpoints. The relative order of setTimeout and setImmediate can depend on the scheduling context.

JAVASCRIPT
setTimeout(() => console.log('timer'));
setImmediate(() => console.log('immediate'));
Promise.resolve().then(() => console.log('promise microtask'));

// Do not infer a universal timer vs. immediate order
// from one run; their order depends on scheduling context.
                  

Still so complicated?

Imagine you’re the only cashier at a store. You serve one customer at a time (the call stack). When a customer needs to grab something, you tell them to step aside (macrotask). But before you call the next one, you handle quick questions like ‘Can I get a receipt?’ (microtasks). You repeat this all day — that’s the event loop!

Summary

  • A browser task runs to completion on the main thread.
  • After that task, the browser drains the microtask queue until it is empty.
  • The browser may then take a rendering opportunity before selecting another task.
  • A zero-delay timer is eligible for a later task; it is not immediate.
  • Self-sustaining microtasks can delay both rendering and later tasks.
  • Node.js has additional queues and phases, so treat it as a separate runtime model.

Guides
Preparing for interviews?