JS_CORE // Interview Survival Kit

One waiter, a whole restaurant, and zero waiting around.

Five concepts that confuse everyone — runtime, single-threaded, async, non-blocking, and the task queue — explained with one story you won't forget.

The one story to remember

Picture a small restaurant with exactly one waiter. That waiter is JavaScript. He can only carry one plate at a time (single-threaded). But he never stands frozen at the kitchen waiting for food to cook — he takes your order, hands it to the kitchen, and goes serve other tables (non-blocking / async). When a dish is ready, the kitchen rings a bell and the order joins a line (task queue), and the waiter picks it up the moment he's free. Every concept below is just one part of this restaurant.

01

JavaScript is a single-threaded language

Technical definition Single-threaded means JavaScript has one call stack and executes one statement at a time, in order.

Single-threaded means JavaScript has one "hand" to do work — called the call stack. It can only run one piece of code at a time, top to bottom. There is no "do two things at the exact same moment."

Restaurant One waiter. One pair of hands. He physically cannot carry two orders to two tables in the same instant.

The call stack is a stack of tasks. The last thing added is the first thing finished (LIFO — like stacking plates). When a function calls another function, it gets stacked on top; when it finishes, it's removed.

function first() { second(); }
function second() { console.log("done"); }
first();
// stack: first → second → log → (unwind back down)
JS does one thing at a time. If one task is slow and blocks the stack, the whole page freezes. That's exactly why async exists.
02

The JavaScript Runtime

Technical definition The runtime is the full environment that runs JS — the engine plus Web APIs, the task queue, and the event loop.

JavaScript by itself is just a language — it can't set timers, call APIs, or touch the webpage. The runtime is the full environment that wraps the language and gives it superpowers. The two famous runtimes are the browser and Node.js.

A runtime is made of four parts working together:

JS Engine

e.g. V8. Reads & runs your code. Holds the Call Stack + Memory Heap. The actual "waiter."

Web APIs

setTimeout, fetch, DOM, events. The "kitchen" that does slow work in the background.

↓ ↑

Task Queue

The line where finished callbacks wait their turn to run.

Event Loop

The manager. Moves waiting tasks onto the stack — but only when the stack is empty.

Restaurant The engine is the waiter, the Web APIs are the kitchen, the task queue is the line of ready orders, and the event loop is the manager who tells the waiter "you're free now — go grab the next ready plate."
The engine (V8) runs JS. The runtime = engine + Web APIs + task queue + event loop. Timers and fetch are NOT part of the language — the runtime provides them.
03

Asynchronous & Non-blocking

Technical definition Asynchronous code starts a task and continues without waiting for it; non-blocking means slow work never freezes the single thread.

Synchronous = each line waits for the line before it to finish. Asynchronous = JS can start a slow task, set it aside, and keep running the rest of the code without waiting. Non-blocking is the result: slow tasks never freeze the single thread.

Restaurant A blocking waiter would give the kitchen your order and then stand there for 20 minutes doing nothing until it's cooked. A non-blocking waiter hands off the order and immediately goes serve five other tables. Same one waiter — just never idle.

How it works: when JS hits something slow (a timer, a network fetch, reading a file), it hands that task to the Web APIs and immediately moves on. The slow work happens outside the single thread. When it's done, its callback gets queued to run later.

console.log("1: order taken");

setTimeout(() => {
  console.log("3: food is ready (later)");
}, 2000);

console.log("2: serving other tables");

// Output:
// 1: order taken
// 2: serving other tables
// 3: food is ready (later)

Notice line 2 prints before line 3, even though it's written after the setTimeout. JS didn't wait — that's non-blocking in action.

Async = start now, finish later, don't wait. Non-blocking = the single thread stays free while slow work happens elsewhere (in the Web APIs).
04

The Task Queue & the Event Loop

Technical definition The task queue holds finished callbacks waiting to run; the event loop moves them onto the call stack whenever the stack is empty.

When a Web API finishes (timer done, data arrived), it can't just barge in — the waiter might be busy. So its callback goes into the task queue (also called the callback queue or macrotask queue) and waits in line.

The event loop has one simple job, repeated forever:

The Event Loop's one rule

"Is the call stack empty? If yes → take the first task from the queue and push it onto the stack. If no → wait."

↻ checks, again, and again, forever
Restaurant Ready dishes pile up at the pass in the order they finished (the queue). The manager (event loop) watches the waiter. The moment the waiter's hands are empty, the manager says "next plate!" — and the waiter delivers it.

The twist: Microtask Queue (Promises)

Technical definition The microtask queue holds Promise callbacks and is fully drained before each single macrotask runs.

There are actually two lines. Promises (.then, async/await) go into a separate, higher-priority microtask queue. The event loop empties the entire microtask queue first, before touching the normal task queue (where setTimeout lives).

Priority order when the stack is empty: 1) run all synchronous code → 2) drain the whole microtask queue (Promises) → 3) then take ONE task from the macrotask queue (setTimeout) → repeat.
★ THE FULL PICTURE

Everything working together

This is the whole machine on one page. Follow the numbered arrows clockwise — that loop is the event loop. Memorize this shape and you can rebuild every answer from it.

JS ENGINE (V8) the single thread · "the one waiter" CALL STACK one task at a time (LIFO) log("hi") second() first() HEAP memory: objects, variables WEB APIs runs slow work in the background · "the kitchen" setTimeout / setInterval ⏱ fetch / network / I/O 🌐 DOM events (click, etc.) 🖱 when finished, each hands its callback to a queue ↓ MICROTASK QUEUE ★ runs FIRST Promises · .then · async/await · queueMicrotask drained completely, every loop MACROTASK / TASK QUEUE setTimeout callbacks · I/O · events only ONE taken per loop EVENT LOOP "is the stack empty? next!" ① hit something slow? offload it → when done → queue callback ③ loop picks next waiting callback ④ push to stack — ONLY if it's empty YOUR CODE runs top-to-bottom on the call stack ↓
Call Stackwhere JS runs your code, one task at a time, last-in-first-out.
Web APIsthe runtime's background workers (timers, fetch, DOM) that handle slow tasks off the thread.
Microtask Queuehigh-priority line for Promises / async-await; fully emptied before any macrotask.
Macrotask Queuenormal line for setTimeout / I/O / events; just one runs per loop, after microtasks.

Read it as a sentence: code runs on the stack → slow tasks get offloaded to Web APIs → finished callbacks wait in a queue → the event loop pushes them back onto the stack the instant it's empty (microtasks before macrotasks). That single sentence is the entire model.

If you can draw these four boxes — Call Stack, Web APIs, Microtask Queue, Macrotask Queue — plus the looping arrow between them, you can answer almost any "how does JS work" question on the spot.
05

The classic output-order question

This exact question shows up in interviews constantly. If you can explain why, you've understood everything above.

console.log("1");

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

Promise.resolve().then(() => console.log("3"));

console.log("4");

// Output: 1, 4, 3, 2

Walk the interviewer through it:

1 & 4 are plain synchronous code → run first, in order.
3 is a Promise → goes to the microtask queue → runs as soon as sync code is done.
2 is setTimeout → goes to the macrotask queue → runs last, even with a 0ms delay, because microtasks always win.

A setTimeout(fn, 0) does not run immediately. 0ms only means "queue it ASAP" — it still waits for all sync code and all microtasks first.

Rapid-fire one-liners

Is JavaScript single or multi-threaded?
Single-threaded — one call stack, one task at a time. Concurrency comes from the runtime (Web APIs + event loop), not from extra threads in the language itself.
If JS is single-threaded, how does it do many things at once?
It offloads slow work to the Web APIs in the runtime, keeps running, and picks up the results later via the event loop and task queue. It only ever looks parallel.
What's the difference between the engine and the runtime?
The engine (e.g. V8) runs the code and holds the call stack. The runtime is the whole environment: engine + Web APIs + task queue + event loop.
What is non-blocking I/O?
Slow input/output (network, files, timers) doesn't halt the single thread — it runs in the background and notifies JS via a callback when finished.
Microtask vs macrotask?
Microtasks = Promises / queueMicrotask, higher priority, fully drained first. Macrotasks = setTimeout / setInterval / I/O, one runs per loop after microtasks are empty.
What does the event loop actually do?
It continuously checks if the call stack is empty, and if so, moves the next queued callback onto the stack to run.