mitmario.dev

fetch

JavaScript Im Browser 4 Min Lesezeit 3 BeispieleLektion 2 von 7

Bis hierher hat dein Code nur mit dem gearbeitet, was schon da war. Jetzt holt er sich etwas.

fetch ist die eingebaute Funktion dafür. Sie schickt eine Anfrage an eine Adresse und liefert ein Promise. Alles, was du in Abschnitt 15 über Promises gelernt hast, gilt hier eins zu eins.

Zwei await

Zwei await, nicht eins
async function holen() {
  // Erstes await: die Antwort ist da.
  const antwort = await fetch("/lernen/api/kurse.json");

  // Zweites await: der Inhalt ist gelesen und ausgepackt.
  const daten = await antwort.json();

  console.log("Typ:", typeof daten, "Anzahl:", daten.length);
  console.log("Erster Eintrag:", JSON.stringify(daten[0]));

  for (const kurs of daten) {
    console.log(kurs.titel, "hat", kurs.stunden, "Stunden");
  }
}

holen();

Der Punkt, an dem jeder beim ersten Mal stolpert: fetch gibt dir nicht die Daten. Es gibt dir eine Antwort.

Das erste await wartet darauf, dass der Server geantwortet hat. Zu diesem Zeitpunkt sind der Status und die Kopfzeilen da, der Inhalt aber noch nicht unbedingt vollständig gelesen.

Das zweite await liest den Inhalt und packt ihn aus. antwort.json() liest den Körper der Antwort zu Ende und schickt ihn durch JSON.parse. Auch das dauert, deshalb ist auch das ein Promise.

Merksatz: Erst die Antwort, dann der Inhalt. Zwei Schritte, zwei await.

Das kannst du dabei mitlesen. Im Reiter Netzwerk steht nach dem Lauf eine Zeile für kurse.json, mit Status, Größe und Dauer: 637 B, 200, ein paar Millisekunden. Sie ist keine Rekonstruktion, sondern eine wirklich abgeschickte Anfrage.

Klapp sie auf, dann steht darin, was die beiden await zurückgegeben haben. Oben die Antwort-Kopfzeilen, darunter der Antwort-Rumpf:

Bei den Kopfzeilen findest du content-type: application/json; charset=utf-8. Das ist genau der Wert, den antwort.headers.get("content-type") dir liefert, und daran erkennt antwort.json(), dass es sich lohnt, JSON.parse darauf loszulassen.

Der Rumpf ist der Text, den der Server geschickt hat. Er steht dort eingerückt, also so, wie du ihn mit JSON.stringify(daten, null, 2) aus Lektion 16.1 selbst schreiben würdest. Über die Leitung ging er in einer Zeile, das Einrücken ist eine Freundlichkeit der Anzeige.

Und ganz unten steht ein curl-Befehl, mit dem du dieselbe Anfrage in einem Terminal wiederholen könntest.

Die drei Zeilen darüber sind etwas anderes, sie tragen die Marke aus der Aufgabe: index.html, styles.css und app.js liegen hier nicht auf einem Server, sie werden direkt in die Seite eingebaut. Über das Netz ging davon nichts, und deshalb sagt die Liste das dazu, statt eine Anfrage zu behaupten, die es nie gab.

Was du sonst noch aus der Antwort holst

Was in der Antwort steckt
async function ansehen() {
  const antwort = await fetch("/lernen/api/kurs.json");

  console.log("ok:", antwort.ok);
  console.log("status:", antwort.status);
  console.log("Inhaltstyp:", antwort.headers.get("content-type"));

  // text() liefert den rohen Text, so wie er über die Leitung kam.
  const roh = await antwort.text();
  console.log("Zeichen insgesamt:", roh.length);
  console.log("Anfang:", roh.slice(0, 40));

  // Achtung: der Körper lässt sich nur einmal lesen.
  try {
    await antwort.json();
  } catch (fehler) {
    console.warn("Zweites Lesen scheitert:", fehler.message);
  }
}

ansehen();

antwort.json() ist der häufigste Fall, aber nicht der einzige:

antwort.text() gibt dir den rohen Text, ohne ihn zu deuten. Nützlich für alles, was kein JSON ist, und zum Nachsehen, wenn json() sich beschwert.

antwort.status ist die Zahl, die der Server zurückgemeldet hat, antwort.ok sagt kurz, ob sie im guten Bereich liegt. Was das genau heißt, ist die nächste Lektion.

antwort.headers.get("content-type") liest eine Kopfzeile.

Eine Eigenheit, die im zweiten Beispiel steht: Der Körper lässt sich nur einmal lesen. Nach text() liefert json() einen Fehler, denn der Inhalt ist durchgelaufen. Wer beides braucht, liest den Text und ruft JSON.parse selbst auf.

Was in der Adresse steht

Eine Adresse kann vollständig sein, mit https:// und Servernamen. Oder sie beginnt mit einem Schrägstrich und meint dann denselben Server, von dem die Seite kam. /lernen/api/kurse.json in diesem Kurs ist so ein Fall.

fetch schickt standardmäßig eine GET-Anfrage, also die Frage nach etwas. Wie man stattdessen etwas hinschickt, steht in Lektion 16.5.

Ein Punkt, der dich später beschäftigen wird: Ein Server muss nicht jeder Seite antworten. Fragt deine Seite bei einem fremden Server nach, entscheidet dessen Antwort darüber, ob dein Code sie sehen darf. Das Stichwort dazu ist CORS, und auch das steht in 16.5.

Das erste Werkzeug bei „es kommt nichts an”

Der Reiter Netzwerk ist das erste, zu dem du greifst, wenn Daten nicht ankommen. Er beantwortet die Frage, die man sonst rät: Ist die Anfrage überhaupt rausgegangen, und was kam zurück?

Drei Fälle sehen dort völlig verschieden aus, und alle drei begegnen dir in den nächsten beiden Lektionen. Eine Anfrage, die gar nicht in der Liste steht: Dein Code hat sie nie abgeschickt, such bei dir. Eine Anfrage mit einem Status ab 400: Sie ist angekommen, der Server sagt nur nein. Eine Anfrage, die fehlgeschlagen heißt und gar keinen Status hat: Sie kam nie bei einem Server an.

Genau denselben Reiter haben auch die Entwicklerwerkzeuge deines Browsers. Öffne sie mit F12 und wechsle auf Netzwerk: dieselben Angaben, dieselbe Arbeitsweise, nur für jede Seite im Netz statt für die eine daneben. Was du hier lernst, nimmst du also mit.

Das vergessene zweite await

Das vergessene zweite await
async function falsch() {
  const daten = await fetch("/lernen/api/kurse.json");

  // Sieht aus wie die Liste, ist aber die Antwort.
  console.log("Länge:", daten.length);
  console.log("Erster Titel:", daten[0]);
  console.log("Ist es ein Array?", Array.isArray(daten));
}

async function richtig() {
  const antwort = await fetch("/lernen/api/kurse.json");
  const daten = await antwort.json();

  console.log("Länge:", daten.length);
  console.log("Erster Titel:", daten[0].titel);
  console.log("Ist es ein Array?", Array.isArray(daten));
}

falsch().then(richtig);

Das Gegenstück zum vergessenen await aus Lektion 15.5, und es sieht genauso harmlos aus.

Wer nur einmal await schreibt, hat die Antwort in der Variablen und nicht die Daten. Und weil eine Antwort ein Objekt ist, wirft nichts. Du bekommst undefined für daten.length, undefined für daten[0] und ein false bei Array.isArray.

Wenn eine geladene Liste unerklärlich leer wirkt, sieh als Erstes nach, ob das zweite await da ist.

Zum Mitnehmen

fetch liefert dir eine Antwort, nicht den Inhalt. Deshalb stehen dort zwei await hintereinander und nicht eins.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

Was in dieser Lektion steckt

  • Artikel mit 3 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe im Editor, direkt im Browser geprüft

    Öffnet sich mit dem Basis Konto.