Abschnitt 16 · Lektion 6
Synthese: die Liste aus dem Netz
Dieser Abschnitt hat dir vier Bausteine gegeben: JSON, fetch, die Fehlerbehandlung und die vier Zustände. Jetzt liegen sie zusammen an einer Stelle.
Die Reihenfolge
Jede ladende Ansicht folgt demselben Ablauf:
Zustand auf „lädt”. Laden. Prüfen, ob die Antwort brauchbar ist. Umformen. Zeichnen. Zustand zurücksetzen. Und ein catch außen herum, das den vierten Zustand bedient.
Diese Reihenfolge ändert sich nie, egal was geladen wird. Was sich ändert, ist nur die Umformung in der Mitte.
Drei Schichten statt einer Funktion
<p id="status">Bereit.</p>
<ul id="liste"></ul>
<button id="los" type="button">Laden</button> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
p {
margin: 0 0 0.75rem;
}
button,
input,
select,
textarea {
font: inherit;
}
button {
padding: 0.4rem 0.8rem;
border: 1px solid #d6d3d1;
border-radius: 6px;
background: #fafaf9;
cursor: pointer;
} const statusFeld = document.getElementById("status");
const liste = document.getElementById("liste");
// Schicht 1: holen. Weiß nichts von der Seite.
async function holen(adresse) {
const antwort = await fetch(adresse);
if (!antwort.ok) {
throw new Error(`Server meldet ${antwort.status}`);
}
return await antwort.json();
}
// Schicht 2: umformen. Weiß nichts vom Netz und nichts von
// der Seite. Eine gewöhnliche Funktion über einer Liste.
function nurFreie(kurse) {
return kurse
.filter((kurs) => kurs.frei)
.sort((a, b) => a.titel.localeCompare(b.titel, "de"));
}
// Schicht 3: zeichnen. Weiß nichts vom Netz.
function zeichnen(kurse) {
liste.innerHTML = "";
for (const kurs of kurse) {
const eintrag = document.createElement("li");
eintrag.textContent = `${kurs.titel} (${kurs.stunden} h)`;
liste.append(eintrag);
}
}
async function anzeigen() {
statusFeld.textContent = "Lädt ...";
try {
const roh = await holen("/lernen/api/katalog.json");
const kurse = nurFreie(roh);
zeichnen(kurse);
statusFeld.textContent = kurse.length === 0 ? "Keine Einträge" : "";
} catch (fehler) {
zeichnen([]);
statusFeld.textContent = "Konnte nicht geladen werden.";
}
}
document.getElementById("los").addEventListener("click", anzeigen); Man könnte alles in eine einzige async-Funktion schreiben. Die Versuchung ist groß, denn es liest sich von oben nach unten.
Trotzdem stehen im ersten Beispiel drei Funktionen nebeneinander, und jede weiß so wenig wie möglich:
holen kennt Adressen und Antworten. Es weiß nicht, was mit den Daten passiert.
nurFreie kennt nur Listen. Es weiß nichts vom Netz und nichts von der Seite. Man könnte es in ein anderes Projekt kopieren.
zeichnen kennt nur die Seite. Es weiß nicht, woher die Daten kommen.
Und anzeigen verbindet die drei und führt den Zustand.
Warum die Umformung eine eigene Funktion ist
<p>Kein fetch, keine Wartezeit. Nur die Umformung.</p> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
p {
margin: 0 0 0.75rem;
} function nurFreie(kurse) {
return kurse
.filter((kurs) => kurs.frei)
.sort((a, b) => a.titel.localeCompare(b.titel, "de"));
}
// Ein fester Datensatz reicht. Er muss nicht echt sein, er
// muss nur die Fälle enthalten, die dich interessieren.
const probe = [
{ titel: "Node", frei: false },
{ titel: "HTML", frei: true },
{ titel: "CSS", frei: true },
];
console.log("Ergebnis:", JSON.stringify(nurFreie(probe)));
console.log("Anzahl:", nurFreie(probe).length);
// Und die Randfälle, die man sonst nie zu Gesicht bekommt.
console.log("Leere Liste:", JSON.stringify(nurFreie([])));
console.log("Nichts frei:", JSON.stringify(nurFreie([{ titel: "Git", frei: false }]))); Das ist der eigentliche Gewinn der Aufteilung, und er zeigt sich, sobald etwas nicht stimmt.
Eine Umformung, die nichts vom Netz weiß, kannst du mit einem festen Datensatz aufrufen. Sofort, ohne Wartezeit, ohne Server, so oft du willst. Du siehst in derselben Sekunde, ob sie das Richtige tut.
Und zwar wörtlich. nurFreie steht im zweiten Beispiel bereit, und du kannst es in der Eingabezeile der Console direkt aufrufen, mit Daten, die du dir gerade ausdenkst. Tipp nurFreie([]) ein, und die Antwort ist []. Tipp eine Liste mit einem Eintrag hin, und du siehst, was übrigbleibt. Kein Klick, kein Laden, kein Warten.
Sieh dabei einmal in den Reiter Netzwerk: Dort steht keine einzige Anfrage. Das ist keine Nebensache, sondern der Beweis für den Satz oben. Beim ersten Beispiel musstest du auf einen Knopf drücken, warten und danach in der Liste eine Zeile mit 975 B vorfinden. Hier nicht, denn hier ist kein Netz beteiligt.
Steckt dieselbe Logik mitten in der Ladefunktion, geht das nicht mehr. Dann musst du für jede Änderung laden, klicken, warten und aus dem Ergebnis auf der Seite rückwärts schließen, was die Umformung getan hat.
Im zweiten Beispiel siehst du außerdem, was mit einem festen Datensatz möglich wird: die Randfälle. Die leere Liste und die Liste, in der nichts übrigbleibt. Beide bekommst du vom echten Server nie zu sehen, und beide passieren später trotzdem.
Abschnitt 6 und 11 sind wieder da
Die Umformung selbst ist nichts Neues. Es ist filter aus Lektion 6.6, sort aus 6.8, oft noch map aus 6.5, und dazwischen ein Vergleich.
Das Zeichnen ist Lektion 11.7: Liste leeren, für jeden Eintrag ein Element bauen, einhängen.
Neu an dieser Lektion ist allein, dass die Daten von außen kommen und deshalb ein Zustand dazugehört.
Wenn die Antwort anders aussieht als erwartet
<p>Drei Antworten, von denen nur eine so aussieht wie erwartet.</p> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
p {
margin: 0 0 0.75rem;
} function zaehlen(antwortkoerper) {
// Erst prüfen, dann arbeiten.
if (!Array.isArray(antwortkoerper)) {
console.warn("Keine Liste, sondern:", typeof antwortkoerper);
return 0;
}
return antwortkoerper.length;
}
console.log("Liste:", zaehlen([1, 2, 3]));
console.log("Objekt:", zaehlen({ kurse: [1, 2, 3] }));
console.log("null:", zaehlen(null));
// Ohne die Prüfung sähe das so aus.
try {
console.log("Ohne Prüfung:", null.length);
} catch (fehler) {
console.error("Ohne Prüfung:", fehler.message);
} Du hast die Daten nicht geschrieben. Sie kommen von einem Server, den vielleicht jemand anderes betreut, und der ändert sich, ohne dich zu fragen.
Was gestern eine Liste war, ist heute ein Objekt mit der Liste darin. Was gestern immer eine Zahl war, ist heute manchmal null. Beides passiert ständig.
„Dann stürzt es eben ab” ist keine Antwort darauf, denn was abstürzt, ist die Seite des Nutzers, und der kann nichts dafür.
Der Aufwand dagegen ist klein. Eine Prüfung mit Array.isArray, bevor du über etwas läufst. Ein ?. beim Zugriff auf eine Ebene, die es geben sollte. Ein ?? mit einem sinnvollen Standardwert. Drei Werkzeuge, die du alle schon kennst, aus den Lektionen 2.2, 7.4 und 3.4.
Im dritten Beispiel siehst du beides nebeneinander: die geprüfte Fassung, die eine Null meldet, und die ungeprüfte, die einen TypeError wirft.
Zum Mitnehmen
Laden, umformen und zeichnen sind drei Aufgaben. Wer sie in eine Funktion schreibt, kann keine davon einzeln prüfen.
Jetzt du
Basis Konto, kostenlosZu 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.