Command Palette

Search for a command to run...

Hectal
PHASE 10Intermediate ~20 min· topic 1 of 8

Topic 10.1

Synchronous vs Asynchronous Code

In one line

Synchronous code runs one line after another and each line must finish before the next starts; asynchronous code starts a slow job, carries on, and handles the result later, so the page never freezes while it waits.

Think of it like this

Ordering at a café. In a synchronous café, the cashier takes your order, walks to the machine, makes your coffee, hands it over, and only then serves the next person: the whole queue stands still. In an asynchronous café, the cashier takes your order, gives you a token number and serves the next person while the machine works. When your coffee is ready, your number is called. Same cashier, far less waiting.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Synchronous
Runs in order: each step must finish before the next one starts.
Asynchronous
Starts a job now and handles its result later, without stopping to wait.
Thread
One worker following one list of instructions. A page's JavaScript runs on one main thread.
Main thread
The browser thread that runs your page's JavaScript, handles input and draws the page.
Blocking
Keeping the thread busy so nothing else (clicks, drawing, timers) can happen.
Long task
Any piece of work that keeps the main thread busy for more than 50 milliseconds.
Callback
A function you hand to someone else to call later, when something has happened.
Web Worker
A separate background thread that can run JavaScript without freezing the page, but can't touch the DOM.

Step by step

01Synchronous code: one line at a time

The engine runs each statement to the end before starting the next. If one line takes a long time, every line after it waits.

sync.jswhole filejavascript
console.log("Take order");
makeCoffee(); // suppose this takes 3 seconds of busy work
console.log("Serve next customer"); // waits 3 seconds

02What blocking does to a page

While makeCoffee() runs, the main thread is busy. A click on a button is noted by the browser but its listener can't run. The screen can't repaint, so a spinner freezes mid-turn.

When the busy work ends, all the waiting clicks are handled at once, which feels broken to the user.

03Asynchronous code: start, carry on, finish later

setTimeout asks the browser to run a function after a delay. The browser keeps the timer; JavaScript moves straight to the next line.

async.jswhole filejavascript
console.log("Take order");
setTimeout(() => console.log("Coffee ready"), 3000);
console.log("Serve next customer"); // runs at once
// Take order
// Serve next customer
// (3 seconds later) Coffee ready

04Who does the waiting?

The browser does. Timers, network requests and disk reads are handled by parts of the browser outside your JavaScript thread. When one finishes, the browser puts your callback in a queue, and the event loop runs it when the thread is free.

Who does the waiting?diagram
Rendering diagram…

05Async can't fix heavy computing

Wrapping a two-second loop in setTimeout only moves the freeze to later. Real fixes: do less work, split it into small chunks so the browser can breathe between them, or move it to a Web Worker.

Try it yourself

  1. 1

    Change the loop length

    In the first example, change 300 in the loop to 100 and the check to waited >= 100. Predict the order of the three lines first: does it change?

  2. 2

    Swap the timer delays

    In the second example, make the toast take 400 and the coffee 100. Predict which line prints third, then run it.

  3. 3

    Feel the freeze

    In the third example, press Freeze and then click the counter three times during the freeze. What does the counter show while frozen? What happens right after?

Code & diagrams

Blocking: a busy loop delays everything, even a 0 ms timer New tab

The timer asked for 0 ms, but it couldn't run until the loop gave the thread back. A timer's delay is a **minimum**, never a promise.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

1. Start the timer (0 ms) and the slow work
2. Slow work finished
3. Timer finally ran. Waited at least 300 ms? true
Asynchronous: order now, collect later New tab

Both sync lines run first. The callbacks run later, in order of **when their timers finish**, not the order they were written.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Order a coffee
Read the newspaper while waiting
Toast is ready
Coffee is ready
Feel the freeze (click the counter, then freeze the page) New tab

During the freeze nothing changes on screen. When it ends, all your counter clicks are handled at once.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Wait with a loop instead of a timer

To show a message after 3 seconds, write const s = Date.now(); while (Date.now() - s < 3000) {} and then show it.

terminal
$ the page shows
── what you'll see ──
The whole page freezes for 3 seconds: buttons don't react, text can't be selected, nothing repaints.

Break #2

Expect async code to run in order

Write let data; setTimeout(() => { data = "ready"; }, 100); console.log(data);.

terminal
$ Console
── what you'll see ──
undefined

Myth vs fact

Myth

Async code runs at the same time as your other code, on another thread.

Fact

Your callbacks run on the same single main thread, one at a time. Only the waiting (timers, network) happens elsewhere, in the browser.

Myth

Making a function async makes heavy work faster.

Fact

Async only stops waiting from blocking. A long calculation blocks the thread wherever it runs; split it into chunks or use a Web Worker.

Myth

JavaScript is slow because it is single-threaded.

Fact

One thread plus non-blocking waiting handles thousands of open network requests very well; Node.js servers are built on it. Problems come only from long CPU work on the main thread.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Chrome's DevTools Performance panel marks tasks over 50 ms with a red corner as long tasks. The Interaction to Next Paint (INP) metric (a Core Web Vital since March 2024) measures how long the page takes to respond to clicks and key presses, and long tasks are its main cause.

  • ▸

    To keep a long job responsive, process it in chunks and yield to the browser between chunks, for example with await new Promise((r) => setTimeout(r, 0)). Chromium also has scheduler.yield(), which resumes your work with higher priority than other queued tasks; check support before relying on it.

  • ▸

    Web Workers run real parallel JavaScript, each with its own event loop and global scope (self), talking to the page through postMessage. They can't touch the DOM. SharedArrayBuffer and Atomics allow shared memory, but only on pages that are cross-origin isolated (special security headers).

  • ▸

    Some old browser APIs are synchronous and block: alert, confirm, prompt, synchronous XMLHttpRequest (deprecated on the main thread) and localStorage (a small synchronous read from disk). Prefer async APIs in new code.

Remember this

  1. 1

    Synchronous ("sync") code runs in order, one statement at a time, and each statement must finish before the next begins. Everything you've written so far in this course is synchronous: const total = price * quantity; finishes, then the next line runs. That's easy to reason about: what you read top to bottom is what happens.

  2. 2

    JavaScript in a page runs on one thread. A thread is one worker following one list of instructions. The page's main thread does your JavaScript and a lot of the browser's own work: handling clicks and key presses, working out the layout and painting the screen. While your code is running, that worker can do nothing else.

  3. 3

    Blocking means a piece of code keeps the thread busy for a long time. A loop that runs for two seconds blocks the thread for two seconds: buttons don't respond, text can't be selected, animations stop and timers can't fire. Browsers notice when a page is stuck for many seconds and may offer to stop it ("Page unresponsive"). Any task longer than 50 ms is called a long task and makes the page feel slow.

  4. 4

    Many jobs are mostly waiting: asking a server for data, waiting 3 seconds before hiding a message, waiting for the user to click. If JavaScript sat and waited, the page would freeze the whole time. Instead it uses asynchronous ("async") code: you start the job, hand the browser a function to call when it's done (a callback), and your code carries on at once. The waiting happens outside your JavaScript, inside the browser.

  5. 5

    The browser (the host, from Topic 0.1) provides the async tools: timers (setTimeout, Topic 10.2), network requests (fetch, Phase 11), events (addEventListener, Phase 9) and more. The JavaScript language itself provides promises and async/await (Topics 10.5–10.8), which are tidy ways to handle the results. The rule that decides when each result is handled is the event loop (Topic 10.3).

  6. 6

    Async doesn't make work faster, and it doesn't run your JavaScript in parallel. It only stops waiting from blocking the thread. Heavy computing (sorting a million items, processing an image) still blocks, because it really uses the thread. For that, you split the work into small pieces or move it to a Web Worker: a separate thread with no access to the page.

Explain it without notes

01

What is the difference between synchronous and asynchronous code?

02

What does it mean that JavaScript in the browser is single-threaded, and why doesn't the page freeze while waiting for a server?

03

Why does setTimeout(fn, 0) not run fn immediately?

04

If async doesn't add threads, how do you keep a page responsive during heavy calculation?

Practice

01

Write code that logs Start, then after 200 ms logs Middle, and logs End straight after Start. Predict the order.

02

This code is meant to print Loaded: yes but prints Loaded: no. Fix it without changing the 100 ms delay. let loaded = "no"; setTimeout(() => { loaded = "yes"; }, 100); console.log("Loaded:", loaded);

Trade-offs

  • ↔

    Synchronous code is simpler to read and debug; asynchronous code keeps the page responsive but spreads one job over several moments in time, which makes order and errors harder to follow.

  • ↔

    Splitting heavy work into chunks keeps the page responsive but the total job takes a little longer; a Web Worker keeps the main thread completely free but needs message passing and can't touch the page.

Done when you can

  • I can explain synchronous and asynchronous code with an everyday example.

  • I know the page's JavaScript runs on one main thread that also handles input and drawing.

  • I can recognise blocking code and explain what the user sees.

  • I know that the browser does the waiting and my callback runs later.

  • I know async doesn't speed up heavy computing, and what to do instead.