Students predict that a page shows Loading before fetched data arrives, order the lines that load JSON, find a missing await, add rain to the forecast, load the live forecast with a saved copy as plan B, and find the week's hottest day.
Load these into Waypoints Prepare: choose Load URL and paste the copied address, or download the file and import it. In Prepare, the worksheet's Teacher edition shows the answers; the student copy hides them.
Predict and Run 10 min · Investigate 15 min · Modify 15 min · Make 15 min
An optional lesson at the end of the DOM course. fetch("weather.json") asks for a file (or a web address) and gives back a promise, a value that arrives later. await waits for it, and only works inside an async function. The reply is text; await response.json() turns it into JavaScript objects and arrays. Forgetting the second await is the planted bug: data is then a promise, so data.daily is undefined.
weather.json is a saved week of Canberra temperatures from Open-Meteo, the same file as Data detective's weather lesson in Python (dates are when it was saved). The live task asks Open-Meteo, a free service with no account or key. try … catch loads the saved copy when the live request fails, which is also what happens if the school network blocks the site, so check it before class with the Check your school page.
Weather data by Open-Meteo.com, licensed CC BY 4.0: the credit line stays on the page.
The order of events is the hard part: the rest of the page runs before the data arrives. Ask "has the data arrived yet at this line?" rather than fixing their awaits for them.
| What you will see | A question to ask | The fix |
|---|---|---|
| Red error: Cannot read properties of undefined. | Is there an await before fetch and before response.json()? | Both need await, inside an async function. |
| await is only valid in async functions. | Does the function have async in front? | async function load() { … } |
| The live forecast never shows, only the saved one. | Is the network or the site blocked here? | That is plan B working; try at home, or check the URL is exactly the one given. |
async function load() {
const response = await fetch("weather.json");
const data = await response.json();
document.querySelector("#out").textContent = data.daily.time[0];
}
load();
One possible solution for each task. Other correct programs are fine.
<h1>Canberra this week</h1>
<p id="status">Loading…</p>
<ul id="days"></ul>
<p><small>Weather data by Open-Meteo.com (CC BY 4.0)</small></p>
<script>
async function load() {
const response = await fetch("weather.json");
const data = await response.json();
const days = data.daily;
for (let i = 0; i < days.time.length; i++) {
const li = document.createElement("li");
li.textContent = days.time[i] + ": " + days.temperature_2m_min[i] + " to " + days.temperature_2m_max[i] + " °C, rain " + days.precipitation_sum[i] + " mm";
document.querySelector("#days").append(li);
}
document.querySelector("#status").textContent = "The saved forecast";
}
load();
</script>
<h1>Canberra this week</h1>
<p id="status">Loading…</p>
<ul id="days"></ul>
<p><small>Weather data by Open-Meteo.com (CC BY 4.0)</small></p>
<script>
const URL = "https://api.open-meteo.com/v1/forecast?latitude=-35.28&longitude=149.13&timezone=Australia%2FSydney&daily=temperature_2m_max,temperature_2m_min,precipitation_sum";
async function load() {
let data;
let note;
try {
const response = await fetch(URL);
data = await response.json();
note = "Live forecast from Open-Meteo";
} catch (error) {
const response = await fetch("weather.json");
data = await response.json();
note = "No connection: using the saved forecast";
}
const days = data.daily;
for (let i = 0; i < days.time.length; i++) {
const li = document.createElement("li");
li.textContent = days.time[i] + ": " + days.temperature_2m_min[i] + " to " + days.temperature_2m_max[i] + " °C";
document.querySelector("#days").append(li);
}
document.querySelector("#status").textContent = note;
}
load();
</script>
<h1>Canberra this week</h1>
<p id="status">Loading…</p>
<ul id="days"></ul>
<p><small>Weather data by Open-Meteo.com (CC BY 4.0)</small></p>
<script>
async function load() {
const response = await fetch("weather.json");
const data = await response.json();
const days = data.daily;
for (let i = 0; i < days.time.length; i++) {
const li = document.createElement("li");
li.textContent = days.time[i] + ": " + days.temperature_2m_min[i] + " to " + days.temperature_2m_max[i] + " °C";
document.querySelector("#days").append(li);
}
let best = 0;
for (let i = 1; i < days.time.length; i++) {
if (days.temperature_2m_max[i] > days.temperature_2m_max[best]) {
best = i;
}
}
document.querySelector("#status").textContent = "Hottest: " + days.time[best] + ", " + days.temperature_2m_max[best] + " °C";
}
load();
</script>
Stretch: Show the forecast as bars with widths from the temperatures; add a Refresh button that calls load() again; load a different town by changing the latitude and longitude in the URL.
Support: Give the try and catch written out; students change only what the status line says.
Why does const data = response.json(); without await give an error later?
response.json() gives a promise, not the data; without await the next line uses the promise before the data has arrived.
Apps with Vue: a fetch in a setup function that sets a ref. Data detective does the same in Python with requests. Class idea: list apps on a phone that fetch data, and what each would show with no connection.