← Teacher guide

Data from the web: fetch

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.

The DOM and the browser console · Final challenge: a quiz page · about 55 minutes · Open the student lesson

Classroom resources

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.

Students will be able to

Timing

Predict and Run 10 min · Investigate 15 min · Modify 15 min · Make 15 min

Before class

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.

Running the lesson

  1. Predict: ask what the page could show while the data is on its way.
  2. Run: point out that the list code is the DOM list lesson's, unchanged.
  3. Investigate: before the bug, ask students to say what each await waits for.
  4. Modify: for the live task, turn the Wi-Fi off (or use a wrong URL) to see plan B work.
  5. Make: ask what the loop must remember as it goes (the best day so far).

The hard part (on purpose)

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.

When students get stuck

What you will seeA question to askThe 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.

Answers to the checks

  1. Predict: What does the page show when it opens, and a moment later?
    Loading… first: the page is drawn before the data arrives. Then await gets the reply, the line after it runs, and the text changes.
  2. Put it in order: Put the lines in order: ask for the file, turn the reply into data, then show the first day.
    see the program below
    Each line needs the one before it: you cannot use data before it has arrived and been turned from text into data.
      async function load() {
        const response = await fetch("weather.json");
        const data = await response.json();
        document.querySelector("#out").textContent = data.daily.time[0];
      }
      load();
    
  3. Why await?: Why is there an await before fetch and before response.json()?
    Each one takes time, and await waits for it before the next line runs
    Without await, the next line runs at once, before the reply is there, and gets a promise (a value that arrives later) instead of the data.
  4. Spot the bug: The page stays on Loading… and shows a red error: Cannot read properties of undefined. Click the line that is wrong.
    line 6
    response.json() also takes time. Without await, data is a promise, not the data, so data.daily is undefined. Write await response.json().
  5. Where this leads: A Vue app needs data from the web. Where would the fetch go?
    In a function in setup, which puts the data into a ref when it arrives
    Load it with fetch, then set a ref's .value; the page follows by itself. Apps with Vue shows refs and functions in setup.

Solutions

One possible solution for each task. Other correct programs are fine.

Modify › Rain

<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>

Modify › Live, with a plan B

<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>

the hottest day

<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 and support

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.

Exit ticket

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.

What each step is for

Where it leads

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.

Sources