The Event Loop: Call Stack Queue Microtasks
Visualize how JavaScript executes: the call stack, the task queue, microtasks from Promises, and why the UI never blocks during async operations.
The Event Loop: Call Stack Queue Microtasks is a free Frontend Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Frontend Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
JavaScript Is Single-Threaded
JavaScript runs on a single thread — it can only execute one piece of code at a time. Yet it handles thousands of async operations (network requests, timers, UI events) without blocking. How? The event loop.
The Call Stack
The call stack is where JavaScript keeps track of which function is currently running. When a function is called, a frame is pushed. When it returns, the frame is popped. If the stack is full (infinite recursion), you get a stack overflow.
function c() { /* does something */ }
function b() { c(); }
function a() { b(); }
a();
// Call stack during c():
// c ← top
// b
// a
// (anonymous)Web APIs — Offloading Work
When JavaScript calls an async function (setTimeout, fetch, addEventListener), the work is handed off to a Web API in the browser. The JS engine continues with the next line — it doesn't wait.
console.log('1');
setTimeout(() => console.log('2'), 0); // handed to Web API
console.log('3');
// Output: 1, 3, 2The Task Queue (Macrotask Queue)
When a Web API operation completes (timer fires, fetch resolves), the callback is placed in the task queue (macrotask queue). The event loop moves the callback to the call stack only when the stack is empty.
The Microtask Queue
Promises use a microtask queue — a high-priority queue. After each task completes and before the next task is picked up, all pending microtasks are drained. Microtasks always run before the next macrotask.
console.log('1');
setTimeout(() => console.log('timeout'), 0); // macrotask
Promise.resolve().then(() => console.log('promise')); // microtask
console.log('2');
// Output: 1, 2, promise, timeoutThe Event Loop in Action
The event loop algorithm: 1) Execute the current task from the call stack until empty. 2) Drain the entire microtask queue. 3) Optionally render. 4) Pick the next macrotask and repeat.
queueMicrotask()
queueMicrotask(fn) schedules a callback in the microtask queue without creating a Promise. Rarely needed directly, but useful to understand how libraries implement scheduling.
queueMicrotask(() => console.log('microtask'));
console.log('sync');
// Output: sync, microtaskWhy Long Tasks Freeze the UI
If a synchronous task runs for too long (a tight loop processing large data), the event loop can't process UI events or renders. This freezes the page. Break long work into chunks with setTimeout to yield back to the event loop.
// Bad — blocks the UI for seconds:
for (let i = 0; i < 10_000_000; i++) { /* heavy work */ }
// Better — yield periodically:
async function processInChunks(items) {
for (let i = 0; i < items.length; i++) {
process(items[i]);
if (i % 100 === 0) await new Promise(r => setTimeout(r, 0)); // yield
}
}Web Workers — True Parallelism
For CPU-intensive work, use a Web Worker — a separate thread with its own event loop. Workers communicate with the main thread via postMessage. No shared memory, no race conditions.
requestAnimationFrame
requestAnimationFrame(callback) schedules a callback before the next browser repaint. Use it for JavaScript-driven animations — it synchronises with the display refresh rate (typically 60fps).
function animate(timestamp) {
// update positions based on timestamp
element.style.transform = `translateX(${timestamp * 0.1 % 400}px)`;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);Visualising the Event Loop
The interactive demo at latentflip.com/loupe lets you paste code and watch the call stack, Web API, task queue, and event loop in slow motion. Essential for building intuition.
Quick Check
After a Promise resolves, its .then() callback runs in which queue?
Recap: The Event Loop
JavaScript is single-threaded. Async work goes to Web APIs. Callbacks return via the task queue (setTimeout, fetch callbacks) or microtask queue (Promises). The event loop drains microtasks after each task before the next render or macrotask. Long synchronous code blocks everything.
Frequently asked questions
Is the “The Event Loop: Call Stack Queue Microtasks” lesson free?
Yes — the full text of “The Event Loop: Call Stack Queue Microtasks” is free to read here on the web, and the Frontend Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Frontend Academy course, upgrade to CoddyKit PRO.
What will I learn in “The Event Loop: Call Stack Queue Microtasks”?
Visualize how JavaScript executes: the call stack, the task queue, microtasks from Promises, and why the UI never blocks during async operations. You practise Frontend Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Frontend Academy?
No prior experience is required. Frontend Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “The Event Loop: Call Stack Queue Microtasks” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Frontend Academy lesson?
Yes. Every Frontend Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- The Event Loop: Call Stack Queue Microtasks
- Callbacks and Callback Hell
- Promises: then catch finally Promise.all
- async/await and Error Handling