completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Synchronous vs asynchronous

Learn why blocking JavaScript freezes the page, how browser and Node host APIs wait outside the call stack, and why callbacks run later without interrupting current code.

By the end you can explain
  • 01
    Why long work freezes the pageExplain how a long synchronous job keeps the main thread busy, so the page can't draw or react to clicks.
  • 02
    What the host does for JavaScriptDescribe timers, server requests, and file reads as jobs the host waits for, outside JavaScript.
  • 03
    Why some callbacks run laterPredict the order of printed lines, using the rule that running code is never cut into.

Now or later: the main idea

JavaScript runs your code one line at a time. Most lines finish at once, and then the next line runs. But some jobs take time, like waiting for a timer or for data from a server. This lesson is about one question: does your code wait for the job right now, or does it keep going and deal with the result later?

Two words name the two ways:

  • Synchronous code ("sync" for short) runs now. The next line waits until it is done.
  • Asynchronous code ("async" for short) starts a job now and deals with the result later. The next line does not wait.

In a browser, your JavaScript runs on the main thread. The main thread is the one worker that runs your code. The browser uses the same worker to draw the page and to handle clicks. So if your code keeps the main thread busy, the page can't do anything else.

You met the host in Engines, runtimes & hosts. The host is the program that runs JavaScript, such as a browser or Node. You saw how the host lines up waiting work in The JavaScript runtime at a glance. This lesson uses both ideas with everyday code: timers, click handlers, and callbacks. A callback is a function you pass to other code, so that code can call it. You met callbacks in Function expressions & callbacks.

Real-life analogySync is a phone call; async is a text message

You call a bakery to ask if your cake is ready. You stay on the line and wait until they answer. You can't do anything else. That is synchronous waiting.

Now you send the bakery a text instead. You put your phone away and do other things. When the reply arrives, you read it. That is asynchronous waiting.

In real life: On a phone call, you stay on the line and wait
In JavaScript: Sync code: the next line waits
In real life: You send a text and get on with your day
In JavaScript: Async code: the host waits, and JavaScript runs the next lines
In real life: The reply arrives later, and you read it then
In JavaScript: The callback runs later
The main idea

Synchronous code runs now, from start to end, before anything else runs. Asynchronous code starts a job now. The host does the waiting. Your callback runs later, when the job is done.

Blocking the main thread

INTERACTIVE

Blocking means JavaScript keeps the main thread so busy that the browser can't do its own jobs. While the main thread is blocked, the browser can't:

  • draw changes on the page,
  • scroll smoothly,
  • respond to clicks or key presses.

Any long job in JavaScript does this. A long calculation blocks. So does a loop that does nothing but wait. So does JSON.parse on a huge piece of text. (JSON.parse turns JSON text into a JavaScript value.) In each case, JavaScript is busy now, so everything else waits.

A box called main thread holds a card that says while loop, so it is busy until the loop is done. Three cards, paint, click, and scroll, want a turn and must wait.while loopmain threadbusy until donewant a turnpaintclickscrollmust wait
The page has one main thread. While a loop runs on it, painting the page and handling clicks must wait.

Try it below. The kitchen experiment has two ways to "bake":

  • The synchronous bake runs a loop for just under a second. The loop keeps checking the clock until the time is up.
  • The asynchronous bake asks the host's timer to wait instead. JavaScript stays free.

Watch the counter. It tries to go up ten times a second. The loop is kept short on purpose, so it can't freeze the page for long.

Two ways to wait in the kitchen
Two ways to wait: a loop, or a timerPop out in the code editor (opens in a new tab)JavaScript
function bakeSynchronously() {  const started = performance.now();  while (performance.now() - started < 1000) {    // JavaScript is busy here, so the page can't update.  }  console.log("sync bake done");} function bakeAsynchronously() {  setTimeout(() => {    console.log("async bake done");  }, 1000);}
Live counteridle
Tick 0

The counter tries to go up ten times a second. If the main thread is blocked, the number stops changing.

  • The counter is running. Try the two bake buttons.
Try it yourself

Run the synchronous bake. Then press Reset and run the asynchronous bake. Watch whether the counter moves.

The loop is kept under one second on purpose: long enough to see the freeze, short enough not to bother you.

During the synchronous bake, the counter stops. Even that small number can't be drawn again. The browser may notice your click or key press, but it can't run the code for it until JavaScript is done with the main thread. That is why real websites never wait with a loop like this.

A short loop runs from start to endPop out in the code editor (opens in a new tab)JavaScript
console.log("work starts");
for (let i = 0; i < 3; i = i + 1) {
  console.log("work " + i);
}
console.log("work ends");

Line 1 prints work starts. The loop on lines 2 to 4 prints work 0, work 1, and work 2. Then line 5 prints work ends. Each line waits for the one before it. If the loop ran a million times, the page would wait for all of them.

The host does the waiting

INTERACTIVE

The JavaScript language itself has no timer, no web page, no files, and no network. The host adds them. A host API is a tool the host gives to JavaScript. Some examples:

  • setTimeout runs a function after a delay, in browsers and in Node.
  • fetch asks a server for data.
  • Node has tools that read files.

When you call a host API, JavaScript hands the job to the host. If you pass a callback, the host keeps it for later. It does not run it yet.

Real-life analogyThe host is a personal assistant

You are cooking. You ask your assistant to watch the oven and tell you when the cake is done. You keep chopping vegetables. When the cake is done, the assistant leaves a note on your desk. You read the note when you finish chopping.

In real life: You ask your assistant to watch the oven
In JavaScript: JavaScript calls a host API like setTimeout
In real life: You keep chopping vegetables
In JavaScript: The rest of your script keeps running
In real life: The assistant leaves a note on your desk
In JavaScript: The host lines up the callback to run
The host does the waiting
A timer handed to the hostPop out in the code editor (opens in a new tab)JavaScript
console.log("1. JS starts the recipe");setTimeout(() => {  console.log("3. host puts the callback back");}, 1000);console.log("2. JS keeps cooking");
Who does whatready
JavaScript (you)Starts the timer, then keeps working
Host (the assistant)Counts the 1000 ms, outside JavaScript
Callback (the note)Waits in line until JavaScript is free
Step 1 of 3JavaScript goes on

Press Start. JavaScript will hand a timer to the host, and keep going while the host counts.

This is a picture of the idea, not a view inside the engine. The 1000 ms delay is real.

This is how async code keeps a page quick to react. The waiting happens in the host, not in JavaScript. A timer callback, a click handler, and a handler for server data all work the same way: the host hands them back later. Promises are another way to work with a result that comes later. You'll see them further down.

The script doesn't wait for the timerPop out in the code editor (opens in a new tab)JavaScript
console.log("order placed");
setTimeout(() => console.log("tea ready"), 0);
console.log("next order");

Line 1 prints order placed. Line 2 hands the timer and its callback to the host. JavaScript doesn't wait, so line 3 prints next order. After the script ends, the host hands the callback back, and it prints tea ready.

A box called JavaScript holds a card that says next order. Arrow 1 hands a callback to a browser window, the host. Inside, the callback card tea ready waits 0 ms. Arrow 3 brings it back to JavaScript later.next orderJavaScript1. hands overbrowser (host)tea ready2. waits 0 ms3. hands it back later
JavaScript hands the callback to the host and keeps going, so it prints next order. Later the host hands the callback back, and it prints tea ready.

The waiting happened in the host. The callback itself still runs as JavaScript, on the main thread.

Some callbacks run later

STEP THROUGH

A callback is a function you pass to other code. That other code decides when to call it.

  • Some callbacks run right away. array.map calls its callback once for each item, while map is still running.
  • Other callbacks run later. A timer callback, an event listener, and a handler for server data all wait until the host has something to report. (An event listener is a function that runs when something happens, such as a click.)

The classic test is setTimeout(..., 0). The 0 is the delay in milliseconds. A delay of zero does not mean "run right now". It means "host, hand this callback back as soon as you can, after the current script ends".

A, C, then B
Step 0 of 4Ready
Your turn: follow the blue line

The delay is 0, but the timer callback still runs after the whole script is done. So the order is A, C, B.

Running in
  1. script
Next: line 1
setTimeout(() => {  console.log("B: timer callback");}, 0);console.log("C: end");
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Three cards in the order they print: A from line 1, now. C from line 5, now. B from line 3 inside the timer callback, later.the order they printACBline 1nowline 5nowline 3, the timerlater
Lines 1 and 5 print now, while the script runs. The timer callback on line 3 prints B later, even with a delay of 0.
Zero is not now

setTimeout(callback, 0) still hands the callback to the host. The callback runs later, after the script. That is why A prints, then C, then B.

Sync or async?
  • console.log("ready")
  • setTimeout(() => console.log("ding"), 0)
  • a handler for a fetch reply
  • the callback passed to array.map
  • button.addEventListener("click", handler)
  • JSON.parse("{\"ok\":true}")
Try it yourself
0 of 6 correct

Sort each line of code. Does it run now, while the current script runs? Or later, after the host has something ready?

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Being a function doesn't make a callback async. The code that receives the callback decides when to call it. Knowing this prevents many "why is this value not ready yet?" bugs.

Three ways to run code later

Callbacks, promises, and async/await all do the same thing: start a job now, then deal with its result later. None of them makes the job faster. None of them makes two pieces of JavaScript run at the same time.

Callback: pass a function that gets the resultPop out in the code editor (opens in a new tab)JavaScript
function greetLater(callback) {
  setTimeout(() => callback("Asha"), 0);
}
greetLater((user) => console.log("Hello, " + user));
console.log("message sent");

greetLater receives a function called callback and gives it to the timer. So the call to greetLater on line 4 finishes before the callback runs. That is why line 5 prints message sent first, and then the callback prints Hello, Asha.

Promise: attach a handler with thenPop out in the code editor (opens in a new tab)JavaScript
Promise.resolve("tea").then((drink) => {
  console.log("ready: " + drink);
});
console.log("order placed");

A promise is an object that stands for a result that may arrive later. Promise.resolve("tea") makes a promise that already holds "tea". then attaches a handler: a function that receives the result. Even though the result is already there, then still runs its handler later. That is why the output is order placed, then ready: tea.

async/await: pause one functionPop out in the code editor (opens in a new tab)JavaScript
async function makeOrder() {
  const drink = await Promise.resolve("tea");
  console.log("ready: " + drink);
}
makeOrder();
console.log("order placed");

The async keyword makes a function return a promise. await pauses that one function until the promise has its result. It does not freeze the whole page. While makeOrder is paused, the rest of the script runs, so line 6 prints order placed first.

Which one to use
  • Use a callback for one small "tell me when it's done".
  • Use then to chain promise steps one after another.
  • Use async/await when you wait for several things in a row and want the code to read from top to bottom.

Run to completion: no cutting in

STEP THROUGH

Run to completion means JavaScript finishes the code it is running before it starts anything else. A timer can be ready while a loop is running, but it can't cut in. The timer callback waits until the loop and the rest of the script are done.

This rule makes code easier to follow. While your function runs, no callback can jump in and change its variables halfway through.

The timer can't cut in
Step 0 of 7Ready
Your turn: follow the blue line

The timer was ready almost at once. But JavaScript finished the whole script, loop included, before it ran the callback.

Running in
  1. script
Next: line 1
  console.log("timer callback");}, 0);console.log("loop starts");for (let i = 0; i < 3; i = i + 1) {  console.log("loop " + i);}console.log("loop ends");
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
A console window shows six lines: loop starts, loop 0, loop 1, loop 2, loop ends, and last timer callback. The first five lines are the script, start to end. The timer callback comes after.consoleloop startsloop 0loop 1loop 2loop endstimer callback1. the script,start to end2. then the timer
The timer is ready almost at once, but it cannot cut in. The script prints all five of its lines first.

The table below sums up the two ways. Read each row as a question, then the answer for sync code and for async code.

How sync work and async waiting differ
QuestionSynchronous (now)Asynchronous (later)
Who waits?JavaScript waits, on the main thread.The host waits, outside JavaScript.
Can the page react while it waits?No. The page waits until JavaScript is done.Usually yes. JavaScript is free until the callback runs.
When does the callback run?There may be no callback. The work happens now.Later, after the current script is done.
What are some examples?console.log, JSON.parse, and array.map.setTimeout, click handlers, and handlers for server data.

This also explains the old browser pop-up boxes alert and prompt. While the box is open, the whole page waits. The Talking to the user lesson shows how to use them. For now, remember that they block the main thread, so use them with care.

Where you'll use this

The sync/async split shows up whenever code waits for something. Some examples:

  • a search box that waits until you stop typing, then searches,
  • a loading spinner that spins while data comes back from a server,
  • a progress bar while a file uploads,
  • a Save button that keeps working while your data is sent to a server.
A simple rule
  • Do quick work now, synchronously.
  • Hand real waiting to a host API.
  • If the work itself is huge, split it into small pieces, or move it to a worker. (A worker runs JavaScript away from the main thread.) Then the main thread stays free for the page.

For example, checking one form field can happen now. Asking a server whether a username is free should be async. Drawing a small part of the page can happen now. Reading a giant reply or filtering a huge list may need splitting, so it doesn't block typing.

Search results arrive laterPop out in the code editor (opens in a new tab)JavaScript
function showResults(results) {
  console.log("showing " + results.length + " results");
}

setTimeout(() => showResults(["tea", "toast"]), 0);
console.log("searching...");

Line 6 prints searching... right away. The timer on line 5 stands in for the wait for the server. When the timer is done, its callback passes two items to showResults, which prints showing 2 results. A real app would show the results on the page instead of printing them.

Common mistakes

"Asynchronous means JavaScript runs in parallel."

Not on the main thread. "In parallel" means at the same time. The host can wait for several things at the same time. But your callbacks still run one at a time, on the main thread.

"A zero-delay timer runs right away."

It runs later. The current script, or the callback that is already running, finishes first.

"Every callback is asynchronous."

array.map and forEach call their callbacks right away, synchronously. What matters is the code that calls the callback.

"Async code makes slow work fast."

Waiting for the host can be async. But heavy work in JavaScript still blocks, unless you split it up or move it off the main thread.

"The host can stop my function in the middle."

Run to completion prevents that. A ready callback waits until the current code is done.

Practice: sync, async, and later callbacks

6 EXERCISES
Exercise 1 · Warm-upPredict the order of the logs

Without running the code, write down the order of the three lines it prints. Then check your answer.

Starter codePop out in the code editor (opens in a new tab)JavaScript
console.log("A: start");
setTimeout(() => {
  console.log("B: timer callback");
}, 0);
console.log("C: end");

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeReplace a waiting loop

    This function waits one second with a loop, so it blocks the page. Replace the loop with a host API, so the page keeps working while it waits. Which host API do you use?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    function waitOneSecond() {
      const started = Date.now();
      while (Date.now() - started < 1000) {}
      console.log("done");
    }

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeWhy do alert and prompt freeze the page?

      Do alert and prompt block the main thread? Answer yes or no, then explain.

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeFind callbacks that run later

        Name two kinds of callbacks from this lesson that run later, not right away.

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeCan the timer cut in?

          Can the timer callback interrupt the loop? Answer yes or no, then explain why.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          setTimeout(() => {
            console.log("timer callback");
          }, 0);
          console.log("loop starts");
          for (let i = 0; i < 3; i = i + 1) {
            console.log("loop " + i);
          }
          console.log("loop ends");

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeA search box that keeps working

            A search box should show "searching" now, then show the results when the server replies. What happens first, and what happens later? Why can you keep typing while it waits?

            Starter codePop out in the code editor (opens in a new tab)JavaScript
            function showResults(results) {
              console.log("showing " + results.length + " results");
            }
            
            setTimeout(() => showResults(["tea", "toast"]), 0);
            console.log("searching...");

            Answer, then press Check. Spacing and letter case don’t matter.

              Quiz: check your understanding

              9 QUESTIONS

              Read each explanation, even when you got the answer right. A wrong choice often sounds almost right.

              Lesson quiz · 9 questionsScore: first tries count
              1. Question 1 of 9What does synchronous code mean?

                Choose an answer to see the explanation.

              2. Question 2 of 9In what order do the letters print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                console.log("A");
                setTimeout(() => console.log("B"), 0);
                console.log("C");

                Choose an answer to see the explanation.

              3. Question 3 of 9Who does the waiting for setTimeout?

                Choose an answer to see the explanation.

              4. Question 4 of 9Which callback usually runs right away, while the current code runs?

                Choose an answer to see the explanation.

              5. Question 5 of 9What does run to completion mean?

                Choose an answer to see the explanation.

              6. Question 6 of 9Which sentence about async and await is true?

                Choose an answer to see the explanation.

              7. Question 7 of 9Why can JSON.parse freeze a page when the text is huge?

                Choose an answer to see the explanation.

              8. Question 8 of 9Which rule is safe to rely on?

                Choose an answer to see the explanation.

              9. Question 9 of 9What does this print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                async function makeTea() {
                  const drink = await Promise.resolve("tea");
                  console.log("ready: " + drink);
                }
                makeTea();
                console.log("order placed");

                Choose an answer to see the explanation.

              Key takeaways

              • Synchronous code runs now. The next line waits until it is done.
              • Blocking the main thread freezes the page. The browser draws the page and handles clicks on that same thread.
              • Host APIs, such as timers, events, fetch, and file reads, do the waiting outside JavaScript.
              • Callbacks given to host APIs run later. Callbacks given to map run now.
              • Run to completion: a ready callback waits until the current script or callback is done.

              Remember the one-liner.
              Sync code is like a phone call: you wait on the line. Async code is like a text: the host waits, and you handle the reply later.

              Up next: Timers: setTimeout & setInterval.

              CompleteFrontend Clear concepts. Working examples.