Synchronous vs Asynchronous

Understand the difference between synchronous (blocking) and asynchronous (non-blocking) execution, and why JavaScript needs asynchronous behavior at all.

JavaScript runs on a single thread — it can only do one thing at a time. Yet web pages routinely wait on things that take an unpredictable amount of time: a network request, a file read, a timer. Synchronous vs asynchronous is the distinction that explains how a single-threaded language can stay responsive while those slow things happen.

Synchronous execution: one line at a time, in order

Synchronous code runs top to bottom, one statement at a time, and each statement must finish before the next one starts. If a line takes five seconds to run, the entire program — including the browser tab's ability to repaint the screen or respond to clicks — is frozen for those five seconds.

Most of the JavaScript you have written so far (variable assignments, function calls, loops, arithmetic) is synchronous by default. let x = 5; finishes instantly and the engine moves to the next line without waiting for anything external.

Why blocking is a problem in a single-threaded language

JavaScript, in the browser, runs on one thread — the same thread that handles rendering, layout, and responding to clicks, scrolls, and key presses. If you write a synchronous operation that takes a long time (say, a loop that blocks for several seconds, or — worse — a network request written as if it were instant), that one thread cannot do anything else until the operation completes. The page appears frozen: buttons don't respond, scrolling stutters, animations stop.

Network requests, reading large files, and timers are especially bad candidates for synchronous code because their duration is unpredictable and often far longer than a single frame of animation (about 16 milliseconds). If fetch() were synchronous, every website that loads data from a server would freeze completely until that data arrived.

Asynchronous execution: starting work without waiting for it

Asynchronous code lets you start a slow operation, keep running the rest of your program immediately, and be notified later — via a callback, promise, or await — when the slow operation finishes. The slow work still happens, but it does not block the single thread while it happens.

This is not the same as multi-threading. JavaScript itself is still single-threaded; the slow work (waiting for a network response, waiting for a timer) is handled outside the JavaScript engine, by the browser (or Node.js), and only the result is handed back to JavaScript when it's ready.

A mental model: ordering tasks at a coffee shop

Synchronous is like a single cashier who takes one customer's full order, makes their coffee, hands it over, and only then calls the next customer — nobody else can even be greeted while that coffee is being made.

Asynchronous is like a cashier who takes your order, hands you a buzzer, and immediately serves the next customer while your coffee is being made in the background; when it's ready, your buzzer goes off and you're notified — without the cashier standing idle waiting for your specific cup.

Code Examples

This is purely synchronous code. Each console.log runs to completion before the next line even begins. Expected result: Task A Task B Task C

Every line finishes before the engine looks at the next one — the order of output exactly matches the order of the code.

setTimeout schedules its callback to run later, even with a delay of 0 milliseconds — it does not pause the program to wait. The engine immediately moves on to Task B and Task C, and only handles the timer's callback after all synchronous code has finished. Expected result: Task A Task B Task C Result (async) Notice the asynchronous callback always runs last here, even though it was written second in the source code — this is the core behavior this whole module explains in depth.

setTimeout(..., 0) does not mean "run in 0ms" in the sense of "run next" — it means "run as soon as possible after the current synchronous code and any earlier-queued work is done."

console.log("Task A");
console.log("Task B");
console.log("Task C");
console.log("Task A");

setTimeout(() => {
  console.log("Result (async)");
}, 0);

console.log("Task B");
console.log("Task C");

Synchronous vs Asynchronous

In synchronous code, tasks form a strict queue where each one blocks the next. In asynchronous code, a slow task is started and the program continues immediately; its result shows up separately once it's ready.

Synchronous:
Task A → Task B → Task C
(each task blocks the next until it finishes)

Asynchronous:
Task A ─────────► Result (arrives later)
Task B → Task C
(Task A is started, but B and C run immediately without waiting for it)

Important Rules

JavaScript is single-threaded: only one line of your code executes at any given instant.

Synchronous code blocks — the next line cannot run until the current one finishes.

Asynchronous code does not block — it starts a slow operation and lets the rest of the program continue, delivering its result later.

Network requests, timers, and file I/O are handled asynchronously so the single thread never freezes the page while waiting on them.

Asynchronous is not the same as multi-threaded — JavaScript still runs one thing at a time; the waiting happens outside the JavaScript engine.

Where this shows up

Almost every real web application depends on asynchronous behavior: fetching data from an API, loading images, reading files, waiting for a user's typed input to settle before searching, or waiting a set delay before showing a notification. Getting the synchronous/asynchronous distinction right is the foundation for understanding callbacks, promises, and async/await — the tools covered in the rest of this module.

"setTimeout(fn, 0) runs fn immediately, right after the line above it." setTimeout(fn, 0) queues fn to run only after all current synchronous code has finished executing, no matter how small the delay is.

A delay of 0 does not mean instant — it means "as soon as the call stack is empty," which is always after every synchronous line that comes after the setTimeout call in the source.

"Asynchronous JavaScript means my code runs on multiple threads at once."

JavaScript remains single-threaded even with asynchronous code; the browser or Node.js environment handles the waiting outside the JS engine and hands control back to the single thread only when there's a result.

Confusing 'asynchronous' with 'parallel/multi-threaded' is one of the most common misconceptions for newcomers to JavaScript — true multi-threading requires separate mechanisms like Web Workers.

[Multiple Choice] What does setTimeout(fn, 0) actually guarantee? A. fn runs before any other line of code, instantly B. fn is queued to run only after the current synchronous code finishes, regardless of the 0ms delay C. fn runs on a separate thread in parallel with the rest of the program D. fn never runs because a 0ms delay is invalid

B. fn is queued to run only after the current synchronous code finishes, regardless of the 0ms delay — A delay of 0 still means the callback is deferred until the call stack is clear — it does not run synchronously or immediately.

Write three console.log statements: log "Start", then schedule a setTimeout with a 100ms delay that logs "Delayed", then log "End". Predict the output order before running it.

console.log("Start"); setTimeout(() => console.log("Delayed"), 100); console.log("End"); // Output: Start, End, Delayed

Remember: setTimeout never runs before the synchronous code that follows it has finished, no matter the delay value.