JavaScript · fetch

fetch, JSON i obsługa błędów — lekcja z przykładowymi danymi

fetch, async i await, json(), sprawdzanie ok, try, catch, finally i Promise.all na adresach data: bez serwera. Trzy zadania z przykładowymi danymi.

45 minutPoziom podstawowyPliki i rozwiązania

Materiał redakcyjny TablicaPro · liceum i technikum

const odp = await fetch(adres);
if (!odp.ok) {
  throw new Error("Blad HTTP " + odp.status);
}
return odp.json();
Liczba uczniow: 3
Dane pobrane asynchronicznie.

Strony internetowe rzadko mają wszystkie dane w kodzie — pobierają je z serwera: listę produktów, pogodę, wyniki. Służy do tego funkcja fetch, która działa asynchronicznie: wysyła żądanie, a program nie czeka bezczynnie na odpowiedź. Na lekcji uczniowie pobierają dane z adresów typu data:, które działają bez serwera i internetu, ale zachowują się jak odpowiedź HTTP — dzięki temu mogą ćwiczyć async/await i obsługę błędów w klasie bez dostępu do sieci.

Scenariusz jest przeznaczony na 45 minut dla klasy znającej obiekty, JSON i obsługę błędów w JavaScript — na przykład po lekcji o JSON.

Przygotowanie stanowisk

Potrzebna jest przeglądarka z konsolą (F12) i edytor tekstu. Rozpakuj pakiet z sekcji materiałów do osobnego folderu. W folderze start jest index.html (nie zmieniasz go) oraz app.js z funkcją pokaz, która wypisuje tekst w konsoli i na stronie. Po zapisaniu app.js odśwież stronę. Gotowe rozwiązanie jest w folderze rozwiazanie. Serwer i Node.js nie są wymagane.

Cele i plan lekcji

Po zajęciach uczeń potrafi:

  • pobrać dane funkcją fetch z użyciem async i await;
  • zamienić odpowiedź na obiekt (odpowiedz.json()) i sprawdzić odpowiedz.ok;
  • obsłużyć błąd bloku try ... catch ... finally;
  • wykonać dwa pobrania równolegle przez Promise.all.
CzasPraca na lekcji
0–5 minProblem: dane, które przychodzą po chwili
5–15 minfetch, async i await
15–22 minOdpowiedź JSON, ok, błędy
22–37 minTrzy zadania samodzielne
37–42 minKolejność wykonania i Promise.all
42–45 minPytania kontrolne

Asynchroniczność w trzech zdaniach

fetch(adres) zwraca obietnicę (Promise) — obiekt, który za jakiś czas zostanie spełniony odpowiedzią albo odrzucony błędem. Słowo await przed obietnicą wstrzymuje funkcję async do chwili jej rozstrzygnięcia, ale nie blokuje reszty strony. Funkcja async zawsze zwraca obietnicę, a await można zapisać tylko w jej wnętrzu (albo w module).

Pobieranie danych

async function pobierz(adres) {
  const odpowiedz = await fetch(adres);
  if (!odpowiedz.ok) {
    throw new Error("Blad HTTP " + odpowiedz.status);
  }
  return odpowiedz.json();
}

odpowiedz.json() też zwraca obietnicę — zamienia treść na obiekt. fetch nie zgłasza błędu dla odpowiedzi 404 czy 500 — takie odpowiedzi są „udane” w sensie sieci, dlatego trzeba sprawdzić odpowiedz.ok. Błąd zgłoszą natomiast: brak sieci i uszkodzony JSON (SyntaxError). Błędy łapiemy w try ... catch wokół await. Promise.all([a, b]) czeka na wszystkie obietnice naraz i zwraca tablicę wyników.

W lekcji adresy data:application/json,... zawierają dane wprost w adresie, więc działają bez serwera. W prawdziwej aplikacji w miejscu adresu stoi np. /api/uczniowie, a przeglądarka może zablokować żądanie z innej domeny, jeśli serwer nie zezwoli na to w nagłówkach CORS.

Zadania

Zadanie 1. Pobranie. Napisz funkcję pobierz(adres) sprawdzającą ok i zwracającą dane JSON. Pobierz listę uczniów ze stałej UCZNIOWIE i wypisz ich liczbę oraz imiona.

Zadanie 2. Przetwarzanie. Dla każdego ucznia wypisz średnią z ocen (albo „brak ocen”) i imiona uczniów ze średnią co najmniej 4.

Zadanie 3. Błędy i równoległość. Spróbuj pobrać uszkodzone dane ze stałej ZEPSUTE i obsłuż błąd blokiem try ... catch ... finally. Pobierz dwa razy listę uczniów równolegle (Promise.all).

Dla chętnych: dodaj ograniczenie czasu pobierania i wypisz komunikat, jeśli odpowiedź nie nadejdzie w 2 sekundy (AbortController).

Rozwiązanie i wyniki

function pokaz(...czesci) {
  const tekst = czesci.join(" ");
  console.log(tekst);
  document.querySelector("#wynik").textContent += tekst + "\n";
}

const UCZNIOWIE = 'data:application/json,[{"imie":"Ola","oceny":[4,5,3]},{"imie":"Jan","oceny":[2,3]},{"imie":"Ewa","oceny":[]}]';
const ZEPSUTE = 'data:application/json,{zepsute';

async function pobierz(adres) {
  const odpowiedz = await fetch(adres);
  if (!odpowiedz.ok) {
    throw new Error("Blad HTTP " + odpowiedz.status);
  }
  return odpowiedz.json();
}

function srednia(oceny) {
  return oceny.length === 0 ? null : oceny.reduce((s, o) => s + o, 0) / oceny.length;
}

async function glowna() {
  // Zadanie 1
  const uczniowie = await pobierz(UCZNIOWIE);
  pokaz("Liczba uczniow:", uczniowie.length);
  pokaz(uczniowie.map(u => u.imie).join(", "));

  // Zadanie 2
  for (const u of uczniowie) {
    const s = srednia(u.oceny);
    pokaz(u.imie, s === null ? "brak ocen" : s.toFixed(2));
  }
  const dobrzy = uczniowie.filter(u => (srednia(u.oceny) ?? 0) >= 4);
  pokaz("Srednia co najmniej 4:", dobrzy.map(u => u.imie).join(", "));

  // Zadanie 3
  try {
    await pobierz(ZEPSUTE);
  } catch (blad) {
    pokaz("Blad:", blad.name);
  } finally {
    pokaz("Koniec pobierania");
  }
  const rownolegle = await Promise.all([pobierz(UCZNIOWIE), pobierz(UCZNIOWIE)]);
  pokaz("Rownolegle:", rownolegle.length, rownolegle[0].length);
}

glowna();

Program wypisze:

Liczba uczniow: 3
Ola, Jan, Ewa
Ola 4.00
Jan 2.50
Ewa brak ocen
Srednia co najmniej 4: Ola
Blad: SyntaxError
Koniec pobierania
Rownolegle: 2 3

Typowe błędy

ObjawCo sprawdzić
SyntaxError: Unexpected token przy json()Odpowiedź nie jest poprawnym JSON-em (np. strona błędu HTML).
Wypisuje się Promise { <pending> }Brakuje await przed wywołaniem funkcji async.
SyntaxError: await is only valid in async functionawait użyto poza funkcją async.
Kod po fetch działa za wcześniefetch jest asynchroniczny; kod zależny od danych musi być po await lub w .then.
Błąd 404 nie trafia do catchfetch nie traktuje 404 jak błędu; sprawdź odpowiedz.ok.
Przeglądarka blokuje żądanie (CORS)Serwer nie zezwolił na dostęp z innej domeny; dane z własnego serwera lub adresy data: nie mają tego problemu.

Pytania kontrolne i punktacja

  1. Co zwraca fetch?
  2. Dlaczego trzeba sprawdzić odpowiedz.ok?
  3. Co robi Promise.all?

Odpowiedzi: Obietnicę (Promise), którą spełnia odpowiedź. Bo fetch uznaje odpowiedzi 404 i 500 za udane i nie zgłasza dla nich błędu. Czeka na zakończenie wszystkich podanych obietnic i zwraca tablicę wyników.

Proponowana punktacja (razem 10 punktów): zadanie 1 — funkcja z await, ok i json 3 pkt, wypisanie liczby i imion 1 pkt; zadanie 2 — średnie z obsługą braku ocen 2 pkt, filtrowanie 1 pkt; zadanie 3 — try/catch/finally 2 pkt, Promise.all 1 pkt. Zadanie dla chętnych nie jest wymagane do pełnej oceny.

Materiały do pobrania

Rozpakuj ZIP przed otwarciem plików. Materiały są dostępne bez logowania; publiczne rozwiązanie służy nauce i samodzielnemu sprawdzeniu, a nie poufnemu sprawdzianowi.

Co dalej?

Następny krok to wyświetlanie pobranych danych na stronie (DOM), nagłówki CORS i własne API w PHP. Ten materiał rozwija dział o danych z serwera w kursie JavaScript w TablicaPro, gdzie znajdziesz prezentacje, ćwiczenia i testy do prowadzenia lekcji.

Źródła techniczne: Fetch API w MDN oraz async function. Zobacz też lekcję o localStorage.

Poprowadź kolejne lekcje JavaScript

Pełny kurs: prezentacje, ćwiczenia i testy do pracy z klasą.

Kup dostęp do kursu JavaScript →

Zobacz program kursu