JavaScript Event Loop Interview Questions: Microtasks, Timers and the Call Stack

Reviewed by Mark Dickie · Last updated

4 questions on this page you can answer and see marked on the spot. Go to the first one

JavaScript's event loop is the runtime mechanism that decides which queued callback runs next once the call stack is empty. Interviewers use it to check whether you can predict output order when timers, promises and synchronous code run together, and whether you know why one long synchronous loop freezes a page. Most questions come down to two queues: microtasks, which drain completely after each task, and macrotasks, which run one per turn of the loop.

Which event loop concepts do interviewers test?

ConceptWhat to be able to explain
Call stackWhy a long synchronous function blocks rendering and input until it returns
Microtask queuePromise.then, queueMicrotask and await continuations, drained before the next task
Macrotask queuesetTimeout, setInterval and I/O callbacks, one per turn of the loop
process.nextTickNode-only queue that runs before promise microtasks
requestAnimationFrameBrowser callbacks that run in the rendering step, before style and layout for the frame

In what order does the event loop run queued work?

  1. Run the current script until the call stack is empty.
  2. Drain the microtask queue, including microtasks queued during the drain.
  3. Run one macrotask, such as a timer callback.
  4. Repeat steps 2 and 3, and render between tasks when the browser has a frame due.

Key facts

  • Tarmac has 15 JavaScript interview questions on this topic, 10 of them on this page, at difficulty 1–5 of 5.
  • Tarmac tracked 2,235 job postings asking for JavaScript in October 2026.
  • Roles asking for JavaScript advertise a median base salary of £73,750, across 700 job postings as of October 2026.
  • Tarmac last reviewed these JavaScript interview questions on 2 November 2026.

At a glance

Questions10 shown · 15 in the bank
Difficulty1–5 of 5
FormatsMultiple choice, Find the bug, Multiple answer, Fill in the blank, True / false, Short answer, Ordering, Coding exercise, Code output
Interactive1 run your code against tests, in the app

What you'll review

  1. macrotasks
  2. microtasks
  3. promise ordering

Practice questions

Try one before you open the answer. Pick an option and press Check; it's marked on the spot.

What does the following JavaScript program print, in order?#

Options

Show answer

In JavaScript, a Promise .then callback runs before a setTimeout callback, even when the timeout is 0 ms. The synchronous logs run first, then the microtask queue drains, then the timer macrotask runs. For this program the output is A, D, C, B.

Why:

The two synchronous console.log calls run first, printing A and then D. When the script finishes, the event loop drains the microtask queue before taking any new macrotask, so the Promise .then callback prints C. The setTimeout callback is a macrotask, so it runs last and prints B, even though its delay is 0 ms.

The intent of this code is to log the user's name after fetchUser() resolves. When run, it prints undefined instead of Ada. Which line contains the bug? Give the 1-based line number.#

function fetchUser() {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ name: "Ada" }), 100);
  });
}
async function main() {
  const user = fetchUser();
  console.log(user.name);
}
main();
Show answer

The bug is on line 7.

Why:

fetchUser() returns a Promise object, not the resolved user object. Line 7 assigns that Promise directly to user without waiting for it, so user.name is read from the Promise itself, which has no name property, and undefined is printed. The fix is to write const user = await fetchUser(); on line 7, which suspends main until the Promise settles (the continuation runs as a microtask once the timer resolves the Promise) and binds the resolved object to user. Line 8 only reveals the symptom; the defect is the missing await on line 7.

Consider this script, run as a main script in a browser or Node.js:#

Options

Pick every one that applies.

Show answer

In JavaScript, the event loop finishes all synchronous code, then drains the entire microtask queue, and only then runs a setTimeout callback. For this script the output is A, E, C, D, B, so the timer callback prints last, after both promise and queueMicrotask callbacks.

Why:

The three synchronous logs run first, so A and E print before any queued callback; E therefore precedes C. After the synchronous code finishes, the event loop drains the microtask queue completely before taking the next macrotask. The promise reaction (C) and the queueMicrotask callback (D) are both microtasks, queued in that order, so they print as C then D. The setTimeout callback is a macrotask, so it runs only after the microtask queue is empty, meaning B prints last. The full output is A, E, C, D, B, which makes the statements that C precedes B, D precedes B, and E precedes C true, and the statement that B precedes C false.

What does this script log, in order, when run in Node.js or a browser?#

Show answer

What does this script log, in order, when run in Node.js or a browser?

console.log('start');
setTimeout(() => console.log('timer'), 0);
Promise.resolve().then(() => console.log('promise'));
console.log('end');

The output order is: start, end, promise, timer. Fill in the two blanks.

Why:

The three synchronous calls run first: console.log('start') and console.log('end') print immediately, and the setTimeout callback and the promise reaction are only scheduled. Once the call stack is empty, the event loop drains the microtask queue before taking the next macrotask, so the promise's .then callback logs 'promise'. Only after that does the timer macrotask run and log 'timer'. The resulting order is start, end, promise, timer.

Consider this synchronous script, run in a browser or in Node.js:#

Options

Show answer

True. A promise callback queued in the same synchronous script runs before a setTimeout(..., 0) callback, even when the timer was scheduled first. After the script finishes, the engine drains the whole microtask queue before taking the next macrotask from the timer queue, in both browsers and Node.js.

Why:

The setTimeout callback is queued as a macrotask (timer task), and the promise reaction is queued as a microtask as soon as Promise.resolve() is settled and .then is attached. When the script finishes, the call stack empties and the engine drains the entire microtask queue before it takes the next macrotask. So cb2 runs first, even though the timer was scheduled earlier. The zero delay is clamped to a small minimum, but the timer callback still has to wait for the next macrotask turn, so the order is fixed.

Explain why a Promise.then callback runs before a setTimeout(fn, 0) callback scheduled earlier.#

Show answer

After the current synchronous code finishes, the event loop fully drains the microtask queue before taking the next macrotask. A Promise.then callback is a microtask, while setTimeout schedules a macrotask. So even if the timer was set first, the entire microtask queue empties before any timer callback runs, making the then callback execute first.

Why:

The event loop empties the microtask queue between each macrotask. Promise reactions are microtasks; timers are macrotasks; so a queued microtask always runs before the next timer callback.

This runs top to bottom:#

Put these in order

Show answer

Synchronous code runs first, then microtasks drain before any macrotask, so the order is:

  1. A
  2. D
  3. C
  4. B

A and D are synchronous. The resolved promise's .then (C) is a microtask that drains before the next macrotask, so it runs ahead of the setTimeout callback B, which is a macrotask.

Why:

Synchronous code runs first (A, then D). Microtasks (the resolved promise's .then, C) drain before the next macrotask, so C precedes the setTimeout callback B, which is a macrotask.

Implement taskOrder(tasks): each task is { id, type } with type "sync", "micro" (microtask, e.g. a settled .then), or "macro" (macrotask, e.g. setTimeout(…, 0)), all scheduled from the same synchronous script. Return the ids in the order the event loop runs them, preserving scheduling order within each kind.#

Starter code

function taskOrder(tasks) {
  // TODO: sync first, then microtasks, then macrotasks
  return tasks.map((task) => task.id);
}

Your solution must pass

  • classic a-d-c-b ordering
  • micro beats macro regardless of scheduling order

This one is written and run, not read. Solve it in the app and your code is executed against these tests and the hidden ones.

In what order are the four values logged?#

console.log('a');
setTimeout(() => console.log('b'), 0);
Promise.resolve().then(() => console.log('c'));
console.log('d');
Show answer
a
d
c
b
Why:

Synchronous code runs first (a, d). The microtask queue (the resolved Promise's .then) drains before the next macrotask, so c logs before the setTimeout callback b.

What is logged, one value per line?#

Promise.resolve().then(() => console.log(1)).then(() => console.log(3));
Promise.resolve().then(() => console.log(2)).then(() => console.log(4));
Show answer
1
2
3
4
Why:

Both first .then callbacks are queued as microtasks before either runs, so 1 then 2 log first. Each only schedules its next .then when it completes, so 3 and 4 are queued afterward and run in the next microtask pass, giving 1, 2, 3, 4.

Sources

The official documentation these questions are checked against:

Related interview questions

Job market

See javascript salaries and hiring demand from live job postings.

The other 5 questions

This page shows 10 and marks what you pick. That's as far as a page can go. A free account opens the other 5 and keeps every answer. What you miss comes back until it's right: after a day, then at longer gaps.

Start with this topic

Free · the whole bank · 100 marked answers per 30 days · written feedback on the paid plan

Interview booked? Practise the job ad instead

What moved, monthly

One email a month when the bulletin comes out: what moved in the markets we track, and the new question topics we published. Confirm your address to join. Unsubscribe any time.