Learning Goals
3 minBy the end of this lesson you will be able to:
- Rewrite a
.then()chain as anasyncfunction. - Use
awaitto get the value out of a promise directly. - Explain why
awaitpauses your function but not the page. - Fetch two things one after the other, in four readable lines.
Warm-Up · Read This Out Loud
5 minYour working chain from last lesson, with a second city added. Try saying what it does, in order, without pausing.
fetch(klUrl)
.then((response) => response.json())
.then((data) => {
showKL(data);
return fetch(penangUrl);
})
.then((response) => response.json())
.then(showPenang);Predict together
- Which
.then()receives the Penang response? - Where would a third city go?
Reveal
The fourth one. And a third city means another return fetch(...) buried inside a handler, then two more .then() calls after it.
The code is correct and flat, yet you have to hop about to follow it. Today removes the hopping.
New Concept · Waiting, Written Plainly
12 minWith .then() you leave instructions at the counter and walk away: “when my order is ready, do this”. With await you sit down in the waiting area until your number is called.
Here is the part that matters: you are sitting down, not the whole restaurant. The staff keep serving everyone else.
Two keywords
async function loadWeather() {
const response = await fetch(url);
const data = await response.json();
console.log(data.current.temperature_2m);
}
loadWeather();asyncbefore a function means “this one may wait”.awaitbefore a promise means “pause here, then give me the value”.
await fetch(url) gives you the Response itself, not a promise. The unwrapping the .then() did for you now happens on the line.
With .then()
fetch(url)
.then((response) => response.json())
.then((data) => {
show(data);
});Values arrive as arguments to handlers.
With await
const response = await fetch(url);
const data = await response.json();
show(data);Values land in ordinary variables.
Identical behaviour. Identical promises underneath. Only the writing changed — which is why last lesson had to come first.
Three rules that catch people out
awaitonly works inside anasyncfunction. Use it anywhere else in your script and you get aSyntaxError.- An
asyncfunction always returns a promise — even if you return a plain number. So calling one gives you a promise, not the value. - Calling it does not wait for it.
loadWeather()starts the work and moves on immediately, exactly likefetchitself.
It does not freeze the page
The word “await” sounds like the blocking loop from Lesson 19. It is the opposite.
At an await, your function steps out of the lane and lets everything else run. Buttons still click, animations still play. When the promise settles, your function picks up where it left off.
Why it matters
Nearly all modern JavaScript is written this way. You will still read plenty of .then() in other people's code, so you need both — but this is the one you will write.
Worked Example · The Same Page, Rewritten
12 minTake the weather page from last lesson and convert it. Keep the old version in a comment so you can compare.
Step 1 — Wrap the work in an async function
async function loadWeather() {
const response = await fetch(url);
const data = await response.json();
output.textContent = `${data.current.temperature_2m} °C`;
}Four lines replace the chain. Read them downwards — that is the whole point.
Step 2 — Call it from the click
load.addEventListener('click', () => {
status.textContent = 'Loading...';
loadWeather();
});The listener itself needs no async, because it does not await anything. Only the function that waits is marked.
Step 3 — Prove the page is still alive
Add the counter button from Lesson 19 beside it. Click Get the weather, then hammer the counter while it loads.
The number keeps climbing. Compare that with the blocking loop in Lesson 19, where it froze solid. Same word in English, opposite behaviour in code.
Step 4 — Two cities, read downwards
async function loadBoth() {
const klResponse = await fetch(klUrl);
const klData = await klResponse.json();
klOutput.textContent = `KL: ${klData.current.temperature_2m} °C`;
const pgResponse = await fetch(penangUrl);
const pgData = await pgResponse.json();
pgOutput.textContent = `Penang: ${pgData.current.temperature_2m} °C`;
}What changed? Compare this with the Warm-Up chain. A third city is now three more lines at the bottom — no hopping, no nesting, nothing to rearrange.
Step 5 — The trap: an async function returns a promise
async function getTemperature() {
const response = await fetch(url);
const data = await response.json();
return data.current.temperature_2m;
}
const temperature = getTemperature();
console.log(temperature);Promise { <pending> }That log should feel familiar — it is Lesson 20's Warm-Up all over again. The return inside an async function fulfils its promise; it does not hand the value out.
async function show() {
const temperature = await getTemperature();
console.log(temperature);
}
show();29.8To unwrap a promise you must await it, and to await it you must be inside an async function. Every escape from that leads back to the same rule.
One thing still missing
There is no .catch() anywhere now. If the fetch fails, this code has nothing to say. Fixing that is the next lesson, and it closes the section.
Try It Yourself
13 minWork in your rewritten weather page.
Task 1 — Convert the countdown
Take the
wait()promise from Lesson 22 and write the 3-2-1 countdown as anasyncfunction with threeawait wait(1000)lines between the updates.Compare it with the chained version. Which reads better?
Task 2 — Break it on purpose
Put an
awaitat the top level of your script, outside any function. Read the error message and write down exactly what it says.Then move it back inside and confirm the error clears.
Task 3 — One function, any city
Write
async function getTemperature(latitude, longitude)that builds its own URL and returns just the number.Call it three times for KL, Penang and Johor Bahru, awaiting each, and show the three results. Lesson 8's parameters and Lesson 3's template literals do the rest.
🔥 Mini-Challenge · The Await That Waits for Nothing
8 minNurul's page shows [object Promise] °C and her console reports an error before anything runs. Find three mistakes.
// nurul-weather.js — buggy
const output = document.querySelector('#output');
function loadWeather() {
const response = await fetch(url);
const data = response.json();
return data.current.temperature_2m;
}
output.textContent = `${loadWeather()} °C`;It works if: the paragraph shows something like 29.8 °C, with no error in the console.
Reveal the answer
Mistake 1 — await without async.
loadWeather is a plain function, so the await inside it is a syntax error. Nothing in the file runs at all — that is why the error appears before anything else. Add async before function.
Mistake 2 — a missing await.
response.json() returns a promise too. Without await, data holds the promise, so data.current is undefined and reading .temperature_2m from it throws.
Rule of thumb: if a line gives you something you cannot use, check whether you forgot to await it.
Mistake 3 — using the function's return value directly.
loadWeather() returns a promise, and dropping a promise into a template literal gives [object Promise]. It must be awaited, which means the calling code has to be inside an async function too.
// nurul-weather.js — fixed
const output = document.querySelector('#output');
async function loadWeather() {
const response = await fetch(url);
const data = await response.json();
return data.current.temperature_2m;
}
async function show() {
const temperature = await loadWeather();
output.textContent = `${temperature} °C`;
}
show();[object Promise] on screen always means the same thing: a missing await, or a missing .then().
Recap
3 minasyncmarks a function that may wait;awaitunwraps a promise.- Same promises as Lesson 22 — only the writing is different.
awaitworks only inside anasyncfunction.- An
asyncfunction always returns a promise, whatever you return inside it. - Calling one does not wait for it — you must
awaitthe call. awaitpauses your function, not the page. Nothing freezes.- Values land in ordinary variables, so steps read straight down the page.
[object Promise]on screen means a missingawait.
New words
- async function — a function allowed to pause, which always returns a promise.
- await — pause until a promise settles, then take its value.
Where this is going
Your code is readable but fragile — one failed request and it stops silently. The last lesson of Section C makes it honest with the user when things go wrong.
📦 Homework
4 min to brief · ~20 min to doRequired
- Convert your Lesson 21 stall-menu page to
async/await. It should fetchmenu.json, await the parse, and render the dishes. - Keep the old
.then()version commented out above it. Bring the file, and be ready to say which you would rather change in six months.
Optional stretch
- Time it. Log before and after two awaited fetches, then look up
Promise.all()and describe how it would make them run together instead. - Write an
asyncfunction that returns a plain string, and prove withtypeofthat calling it still gives you a promise.