Frontend Interview Guide: How Macro‑ and Micro‑Tasks Determine JavaScript Execution Order

The article explains JavaScript’s single‑threaded event loop, classifies macro‑ and micro‑tasks, details their execution order, and walks through classic and advanced code snippets to show how interview questions about output are solved in typical front‑end interview scenarios.

CodeNotes
CodeNotes
CodeNotes
Frontend Interview Guide: How Macro‑ and Micro‑Tasks Determine JavaScript Execution Order

JavaScript is Single‑Threaded

JS runs on a single main thread; time‑consuming operations are handed to Web APIs and their callbacks are queued for later execution.

Task Classification

Macro Task:
  script (the whole script is the first macro task)
  setTimeout / setInterval
  I/O (Node.js)
  requestAnimationFrame

Micro Task:
  Promise.then / .catch / .finally
  queueMicrotask()
  MutationObserver
  async/await (code after await)

Execution Order Rules

1. Execute the current macro task (e.g., the whole script)
2. Execute all micro‑tasks in the queue (including those added while emptying)
3. Render if needed
4. Take the next macro task and repeat 1‑3

In short: after each macro task finishes, the micro‑task queue is emptied immediately.

Classic Interview Example

console.log('1');

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

Promise.resolve().then(() => {
  console.log('3');
  Promise.resolve().then(() => console.log('4'));  // micro‑task added inside a micro‑task
});

console.log('5');

// Analysis:
// Synchronous: 1, 5
// Micro‑tasks (cleared): 3, 4 (4 is added during 3 and still cleared this round)
// Macro‑task: 2

// Output: 1, 5, 3, 4, 2

The Essence of async/await

async function foo() {
  console.log('A');
  await bar();        // after bar() finishes, the code after await goes to the micro‑task queue
  console.log('C');  // micro‑task
}

async function bar() {
  console.log('B');
}

console.log('start');
foo();
console.log('end');

// Output: start, A, B, end, C
// Explanation: await bar() turns the following console.log('C') into a micro‑task, so the synchronous end runs first.

Advanced Combined Example

console.log('1');

setTimeout(() => {
  console.log('2');
  Promise.resolve().then(() => console.log('3'));
}, 0);

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

console.log('6');

// Execution analysis:
// Synchronous: 1, 6
// Micro‑tasks: 4, 5
// Second macro task (setTimeout): 2
// Micro‑task inside that macro task: 3

// Output: 1, 6, 4, 5, 2, 3

Solution Method for Interview Questions

Identify synchronous code and list its output order.

Identify all micro‑tasks, then clear the micro‑task queue in the order they were enqueued (including any newly added).

Proceed to the next macro task and repeat steps 2‑3.

Key Takeaways

JS is single‑threaded; the event loop orders asynchronous code.

Micro‑tasks (Promise, queueMicrotask) run before macro‑tasks (setTimeout, setInterval).

Typical solving approach: synchronous → empty micro‑task queue → next macro task, repeat.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaScriptAsync/AwaitEvent LoopMacro TaskMicro TaskFrontend Interview
CodeNotes
Written by

CodeNotes

Discuss code and AI, and document daily life and personal growth.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.