JavaScript Advanced: Closures, Async and Modules
The Event Loop
In this lesson you will
- Explain why JavaScript is single-threaded but not blocking
- Describe the call stack, task queue and microtask queue
- Predict the order of synchronous code, promises and timers
Introduction
JavaScript runs your code on a single thread: it can do only one thing at a time. Yet a page can handle clicks while a timer ticks and a download runs. The secret is the event loop, and understanding it explains many "why did it print in that order?" puzzles.
The simple explanation
JavaScript finishes what it is doing, then picks up the next waiting job.
Imagine a cook with a single pair of hands. They can only chop one thing at a time. But they use timers and an oven: put the bread in, set a timer, and chop vegetables while it bakes. When the timer rings, the cook finishes the current chop, then handles the bread.
The pieces
- Call stack: the code running right now. It runs to completion.
- Task queue (macrotasks): waiting jobs such as timer callbacks and event handlers.
- Microtask queue: higher-priority jobs, mainly promise callbacks (
.then,await). - Event loop: when the stack is empty, it runs all microtasks first, then takes the next task.
Quick quiz
Which runs first once the call stack is empty?
Predict the order
Press Run. The order is:
1: start: synchronous code runs first.2: end: still synchronous.3: promise: a microtask, so it runs as soon as the stack is empty.4: timeout: a task, so it waits for its turn, even with a delay of0.
A setTimeout of 0 does not mean "right now". It means "as soon as possible, after the current work and all microtasks".
Blocking the loop
Because everything shares one thread, long synchronous work freezes everything else.
The timer is ready at once, but it cannot run until slow() finishes. In a real page, that would mean buttons that do not respond. Keep heavy work short, or move it into a Web Worker (a separate thread, which is what this site uses to run your code).
async/await and the loop
An await splits a function into two parts. The first part runs now. The rest is queued as a microtask for when the awaited promise settles.
B prints before C, because the code after await has to wait for the stack to clear.
Quick quiz
A program sets a timer of 0 ms that logs a, queues a promise callback that logs b, then logs c directly. In what order do they print?
Try it yourself
Predict the output of this program before you run it. Then run it and check. Add another console.log and another setTimeout and predict again.
Summary
- JavaScript is single-threaded. The event loop runs queued jobs when the stack is empty.
- Microtasks (promise callbacks) run before the next task (timers, events).
setTimeout(fn, 0)means after the current code and all microtasks.- Long synchronous code blocks everything. Keep it short or use a worker.
Lesson complete
+20 XP
Nice work. Keep going while it is fresh.
Sources and further reading