fetch() – načítání dat ze serveru

00:00:00

Doposud JavaScript pracoval jen s tím, co je přímo na stránce. Funkce fetch() otevírá nový svět: umožňuje načítat data ze serveru nebo z externího API bez obnovení celé stránky. Díky ní může web zobrazit aktuální počasí, načíst výsledky vyhledávání nebo odeslat formulář – a vše tiše na pozadí, bez přerušení práce uživatele.

Co je fetch() a jak funguje?

fetch() je zabudovaná funkce prohlížeče pro síťové požadavky. Posílá HTTP požadavek na zadanou URL a vrátí Promise – příslib budoucí odpovědi. Síťový požadavek totiž chvíli trvá a JavaScript nesmí mezitím zamrznout.

Základní použití:

fetch('https://api.example.com/data')
  .then(function (odpoved) {
    // Požadavek proběhl – ale data jsou zatím surová odpověď
    return odpoved.json(); // převede tělo odpovědi na JavaScriptový objekt
  })
  .then(function (data) {
    // Zde máme hotová data jako objekt nebo pole
    console.log(data);
  })
  .catch(function (chyba) {
    // Síťová chyba nebo jiný problém
    console.error('Chyba při načítání:', chyba);
  });

Metoda .then() říká: „Až bude odpověď připravena, udělej toto.\" Metoda .catch() zachytí případnou chybu. Toto je tzv. řetězení Promises.

Modernější zápis – async/await

Novější a přehlednější způsob práce s Promises je klíčové slovo async/await. Kód pak vypadá téměř jako synchronní, přestože čeká na síťový požadavek:

async function nactiData() {
  try {
    let odpoved = await fetch('https://api.example.com/data');

    // Ověření HTTP statusu
    if (!odpoved.ok) {
      throw new Error('Server vrátil chybu: ' + odpoved.status);
    }

    let data = await odpoved.json();
    console.log(data);
    return data;

  } catch (chyba) {
    console.error('Chyba:', chyba.message);
  }
}

// Zavolání funkce
nactiData();

Klíčové slovo await lze použít jen uvnitř funkce označené async. Blok try/catch nahrazuje .catch() z předchozího zápisu.

Načítání JSON dat z API

Nejčastější použití fetch() je načtení dat z veřejného API. Ukázka s veřejným API pro náhodné citáty:

async function nactiCitat() {
  let oblast = document.getElementById('citat-oblast');
  oblast.textContent = 'Načítám citát…';

  try {
    let odpoved = await fetch('https://api.quotable.io/random');
    if (!odpoved.ok) throw new Error('HTTP ' + odpoved.status);

    let data = await odpoved.json();

    // data.content = text citátu, data.author = autor
    oblast.textContent = `„${data.content}" — ${data.author}`;

  } catch (chyba) {
    oblast.textContent = 'Citát se nepodařilo načíst.';
  }
}

document.getElementById('btn-nacist').addEventListener('click', nactiCitat);

Odeslání dat na server – metoda POST

Výchozí metoda fetch() je GET (stažení dat). Pro odeslání dat se použije POST s tělem požadavku:

async function odesliFormalur(jmeno, email, zprava) {
  try {
    let odpoved = await fetch('https://formspree.io/f/tvujkod', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ jmeno, email, zprava })
    });

    if (odpoved.ok) {
      document.getElementById('zprava').textContent = 'Odesláno, díky!';
    } else {
      throw new Error('Odeslání se nezdařilo.');
    }

  } catch (chyba) {
    document.getElementById('zprava').textContent = chyba.message;
  }
}

Načítání lokálních souborů

fetch() funguje i pro lokální soubory na stejném serveru – například HTML stránky lekcí, JSON soubory s daty nebo textové soubory:

// Načtení HTML lekce pro vyhledávání (přesně jak funguje web_search.js)
async function nactiLekci(url) {
  try {
    let odpoved = await fetch(url);
    if (!odpoved.ok) return '';

    let html = await odpoved.text(); // .text() místo .json() pro HTML
    return html;

  } catch {
    return '';
  }
}

// Použití
let obsah = await nactiLekci('web/w.css_grid.html');
// obsah je celý HTML soubor jako řetězec

Metoda .text() vrátí tělo odpovědi jako prostý řetězec – hodí se pro HTML, CSV nebo jakýkoliv nJSON obsah.

Načítání dat a přístupnost

Při dynamickém načítání dat je důležité informovat uživatele odečítače o stavu operace:

  • Před načítáním nastav text oblasti na „Načítám…\" – aria-live="polite" oblast ho přečte.
  • Po načtení vlož výsledek do stejné aria-live oblasti – VoiceOver automaticky oznámí změnu.
  • Při chybě zobraz srozumitelnou hlášku v aria-live="assertive" oblasti.
  • Tlačítko pro spuštění načítání by mělo být dočasně označeno jako zaneprázdněné: button.setAttribute('aria-busy', 'true').
async function nactiSData(btn, oblast) {
  btn.setAttribute('aria-busy', 'true');
  btn.disabled = true;
  oblast.textContent = 'Načítám data…';

  try {
    let odpoved = await fetch('…');
    let data = await odpoved.json();
    oblast.textContent = data.text;
  } catch {
    oblast.textContent = 'Nepodařilo se načíst data. Zkus to znovu.';
  } finally {
    btn.setAttribute('aria-busy', 'false');
    btn.disabled = false;
  }
}

Tip od Zdeňka

Zdeněk radí: Funkce fetch() je výkonný nástroj, ale s jednou záludností: HTTP chyby (404, 500) ji samy o sobě nevyhodí do catch – musíš sám zkontrolovat odpoved.ok nebo odpoved.status. Do catch se dostaneš jen při skutečné síťové chybě (žádné připojení, timeout). Tento detail přehlédne spousta začátečníků a pak se diví, proč kód „funguje\" i když server vrátí 404. Vždy proto píši if (!odpoved.ok) throw new Error(…) hned za await fetch() – ušetří to hodiny ladění.