Problem Statement
Explain the JavaScript event loop. How do microtasks and macrotasks differ?
Explanation
The JavaScript event loop is the system that allows JavaScript to handle asynchronous operations even though it runs on a single thread.
JavaScript first runs all synchronous code on the call stack. When asynchronous work finishes—like timers, network requests, or Promises—their callbacks don’t run immediately. Instead, they’re placed into different queues. The event loop constantly checks: “Is the call stack empty?” When it is, it pulls tasks from these queues and executes them.
JavaScript has two important types of async tasks:
1. Microtasks (higher priority):
These include Promise .then() callbacks, queueMicrotask(), and MutationObserver.
Microtasks run right after the current synchronous code finishes, and the event loop will empty the entire microtask queue before moving on.
2. Macrotasks (lower priority):
These include setTimeout, setInterval, DOM events, and I/O callbacks.
The event loop processes one macrotask per cycle, and after each macrotask, it immediately runs all pending microtasks again.
Why the difference matters:
Because microtasks always run before the next macrotask, a Promise callback will run before a setTimeout(..., 0) every time.
Code Solution
SolutionRead Only
// Event loop example
console.log('1'); // Synchronous
setTimeout(() => console.log('2'), 0); // Macrotask
Promise.resolve().then(() => console.log('3')); // Microtask
console.log('4'); // Synchronous
// Output: 1, 4, 3, 2
// Explanation:
// 1. Sync code: 1, 4
// 2. Microtasks: 3
// 3. Macrotasks: 2
// Complex example
console.log('Start');
setTimeout(() => {
console.log('Timeout 1');
Promise.resolve().then(() => console.log('Promise in timeout'));
}, 0);
Promise.resolve()
.then(() => {
console.log('Promise 1');
setTimeout(() => console.log('Timeout in promise'), 0);
})
.then(() => console.log('Promise 2'));
setTimeout(() => console.log('Timeout 2'), 0);
console.log('End');
// Output:
// Start
// End
// Promise 1
// Promise 2
// Timeout 1
// Promise in timeout
// Timeout in promise
// Timeout 2
// Why Promises before setTimeout?
async function test() {
console.log('A');
setTimeout(() => console.log('B'), 0);
await Promise.resolve();
console.log('C');
setTimeout(() => console.log('D'), 0);
}
test();
console.log('E');
// Output: A, E, C, B, D
// Microtask queue vs Macrotask queue
queueMicrotask(() => console.log('Microtask'));
setTimeout(() => console.log('Macrotask'), 0);
// Microtask runs first
// Event loop phases:
// 1. Execute all synchronous code
// 2. Process all microtasks
// 3. Render (in browser)
// 4. Process one macrotask
// 5. Repeat from step 2