LearnThatStack Ace your next interview
Concurrency & Multithreading · question
Question 7 of 54

How does the JavaScript event loop run async work on one thread?

beginner
← All Concurrency & Multithreading questions
Re-explain
Visual ↓

The JavaScript event loop lets the system do the waiting for async work, then runs each callback on its one thread. It takes the next callback from a queue, runs it to completion, then grabs the next.

When you start a timer, network request, or file read, the runtime hands it to the system or a background pool. Your thread moves on right away.

When the operation finishes, its callback joins a queue. The loop runs it only after the current work returns and the stack is empty. So a ready callback still waits behind the running code.

This model avoids locks, since only one callback runs at a time. The catch is that any slow synchronous function freezes everything. One blocking loop stalls every pending timer, request, and click.

Rewriting in plainer words…

This answer doesn't lend itself to a diagram - it reads best . No credits were charged.

Why there's no diagram: “”

The interactive diagram is below the answer - jump to diagram ↓ · Below it, the related concept . Jump to it ↓

The diagram below the answer is the concept . Jump to it ↓

Tailored explanation · switch back to · ·
What should the new diagram focus on?
Guided practice for Concurrency & Multithreading One question at a time. Answer out loud, get graded, see what you missed.
See it - interactive diagram
A ready timer callback waits for an empty call stack
function onTimer() {
console.log('timer done');
}
function slowWork() {
const t = Date.now() + 50;
while (Date.now() < t) {}
}
console.log('start');
setTimeout(onTimer, 10);
slowWork();
console.log('end');
Call stackruns now
scriptconsole.log
Backgroundsystem does the waiting
empty
Callback queuewaits for an empty stack
empty
Console
start
1step

The script starts on the one thread. This line is synchronous, which means it finishes before the next line starts, so start prints now.

1 / 8

A ready callback waits in the queue until the running code returns and the stack is empty, so one slow function delays everything.

© LearnThatStack - diagrams may not be republished without permission.

Other questions have diagrams like this too. Sign in to ask for one where it's missing - it's free.

Create a free account
How well did you know this?
AI:

Saved in this browser - sign in to keep your review list.

How should your speech become text?

Listening… your words appear above as you speak - tap Stop when you're done.

Recording · cr - tap Stop & transcribe when you're done.

Transcribing with AI…

Voice:

Keep going - a few more words and AI can grade it.

Interview lens How interviewers actually use this question

Likely follow-ups

  • Why does a promise callback run before a setTimeout(fn, 0) that was scheduled earlier in the same code? Name the microtask queue. The loop drains it fully after each task, before it takes the next timer or I/O callback.
  • If only one callback runs at a time, can you still get a race condition? Yes. State can change between an await and the line after it, so a check-then-act split across an await can still interleave.
  • What breaks when one request handler runs a 2-second synchronous loop? Every request, timer and click waits behind it. A strong answer names latency spikes, timeouts, and measuring event-loop delay to find the culprit.
  • How would you run CPU-heavy work without freezing the page or the server? Move it off the main thread with a Web Worker or a Node worker thread, or split it into chunks that yield back to the loop.
  • When would you choose this single-threaded async model over a thread pool? Many concurrent requests that mostly wait on I/O favor it. CPU-bound work or blocking libraries push you toward threads.

What you can say

  1. JavaScript runs your code on one thread, and the event loop lets the system do the waiting while that thread runs one callback at a time.
  2. When you start a timer, a network request or a file read, the runtime hands it to the operating system or a background pool, and your code moves on right away.
  3. When that operation finishes, its callback goes into a queue.
  4. The loop picks up the next callback only after the current code returns and the stack is empty, then runs it to completion, so a ready callback still waits behind running code.
  5. Since only one callback runs at a time, you don't need locks around shared state.
  6. The catch is that any slow synchronous function freezes everything. One blocking loop stalls every pending timer, request and click.

Weak answers to avoid

  • Says JavaScript runs async work in parallel threads Your code never runs in parallel. The system or a background pool does the waiting, and every callback still runs on the one thread. Say who waits and who runs.
  • Thinks a ready callback interrupts running code Callbacks never preempt. A finished timer waits until the stack is empty, which is why setTimeout(fn, 0) is not immediate. Explain run-to-completion.
  • Recites queue names but skips why blocking hurts Naming the call stack and task queue is not enough. The interviewer wants the trade-off. You need no locks, but one slow synchronous function freezes everything.
  • Claims async/await makes slow code non-blocking Marking a function async does not move its CPU work off the thread. A heavy loop inside it still blocks everything. Only the waiting gets handed off.
Interview lens

Likely follow-ups, what you can say, and the weak answers to avoid.

Sign in free to open it Free account - the lens opens as soon as you're back.

Want a quick review of the fundamentals? See the Concurrency & Multithreading cheatsheet.

← Back to all Concurrency & Multithreading questions
Pro · $10/mo

47 of 54 Concurrency & Multithreading answers are in Pro.

Full answers, code samples, and AI explanations that go simpler or deeper. Cancel anytime.

  • Full answers + code
  • AI explanations, simpler or deeper
  • 1,000 AI credits / month
  • Cancel anytime