Start / Seminare / JavaScript Grundlagen & Modernes ECMAScript

Modul

Promises, HTTP-Anfragen und Events

5 Kapitel in diesem Modul-Video · Laufzeit

Vertonung mit synthetischer Stimme · Inhalte redaktionell verantwortet · © 2026 HECKER CONSULTING, alle Rechte vorbehalten

Transkript

Der gesprochene Text dieses Moduls zum Mitlesen, Überfliegen und Durchsuchen. Ein Klick auf einen Zeitstempel springt an die Stelle im Video.

JavaScript — Promises, HTTP und Events

0:00 Im letzten Modul ging es um einen einzelnen asynchronen Vorgang. Der ist überschaubar. Interessant wird es, wenn mehrere gleichzeitig laufen — und einer davon scheitert. Genau da beginnt die Praxis. In diesem Modul koordinieren wir mehrere Vorgänge, holen Daten aus dem Netz, entscheiden zwischen nacheinander und gleichzeitig, greifen auf Dateien zu und reagieren auf Ereignisse.

0:22 Eine einzige Frage zieht sich durch fast alle Kapitel, und ich nenne sie gleich vorweg: Hängt dieser Schritt vom vorigen ab? Von der Antwort hängt hier erstaunlich viel ab.

Asynchron in der Praxis

0:33 Fünf Kapitel, alle handfest. Zuerst die Kombinatoren — Werkzeuge, die mehrere Promises koordinieren, mit einer Auswahltabelle, die wichtiger ist als jede Methodenliste. Dann HTTP-Anfragen, mit einem Satz, der Sie überraschen wird und der in der Praxis für viele Bugs sorgt. Danach die Frage nach nacheinander oder gleichzeitig, an einer Rechnung, die den Unterschied greifbar macht.

0:56 Dateizugriffe folgen, mit zwei Fehlerquellen, die man auseinanderhalten sollte. Und zum Schluss ein anderes Denkmodell: Ereignisse.

Kombinatoren für Promises

1:05 Beginnen wir mit der Koordination mehrerer Vorgänge. Es gibt fünf Werkzeuge dafür, und ich möchte ausdrücklich nicht, dass Sie sie auswendig lernen. Der eigentliche Inhalt ist die Auswahltabelle — also die Frage, welches wann passt. Ein Kombinator koordiniert mehrere Promises: Er nimmt eine Liste entgegen und liefert selbst wieder ein Promise.

1:27 Der entscheidende Satz steht am Ende: Sein Verhalten hängt davon ab, wie viele der Einzelvorgänge erfolgreich sein müssen. Und genau das ist eine fachliche Frage, keine technische. Muss alles klappen, damit die Seite Sinn ergibt? Oder ist ein Teilausfall verkraftbar? Reicht die erste Antwort, die ankommt? Wenn Sie diese Frage beantworten können, ergibt sich der Kombinator von selbst. Wenn nicht, hilft Ihnen die beste Methodenkenntnis nichts.

1:54 Fünf Einträge, und lesen Sie die rechte Spalte als Antworten auf die Frage von eben. Alle müssen gelingen — dann bricht der erste Fehler alles ab. Sie wollen jedes Ergebnis sehen, auch die gescheiterten — dann wird auf alle gewartet und je Vorgang berichtet. Der erste Erfolg genügt — dann werden Fehler ignoriert. Der erste entschiedene Vorgang zählt, egal wie er ausging — das ist das Rennen, typischerweise für Zeitlimits.

2:20 Und der letzte Eintrag startet eine Kette aus möglicherweise synchronem Code. Der Fußtext nennt noch ein fortgeschrittenes Werkzeug, das für den Einstieg nicht nötig ist. Die beiden häufigsten Fälle im direkten Vergleich. Oben: Ein Fehler in einem der Vorgänge lässt den gesamten Aufruf scheitern — Sie bekommen den Fehler und keines der Ergebnisse, auch nicht die erfolgreichen.

2:43 Das ist manchmal genau richtig und manchmal katastrophal. Unten die geduldige Variante: Es wird auf alle gewartet, und jedes Ergebnis trägt eine Statusangabe. Sie sehen also, was geklappt hat und was nicht, und können entsprechend reagieren. Der Preis dafür: Sie müssen diese Statusangabe auch tatsächlich auswerten — sonst arbeiten Sie mit Ergebnissen, die gar keine sind.

3:06 Zwei Szenarien, zwei Entscheidungen — und wie so oft in diesem Kurs ist die Begründung der eigentliche Auftrag. Einmal ist alles nötig, einmal ist Teilerfolg akzeptabel. Der Hinweis nennt den Musterfall für die geduldige Variante: eine Übersichtsseite mit optionalen Kacheln. Wenn das Wetter-Widget nicht lädt, soll deshalb nicht die ganze Seite leer bleiben. Diese Denkweise — welcher Teil ist verzichtbar?

3:30 — ist im Grunde eine Produktfrage, keine Programmierfrage. Und genau deshalb müssen Sie sie stellen, bevor Sie den Kombinator wählen. Die strenge Variante zu nehmen, obwohl ein Teilausfall verkraftbar wäre, ist der häufigste Fall — und er macht Anwendungen unnötig zerbrechlich. Ein einziger langsamer Dienst legt dann alles lahm.

3:50 Der zweite Punkt ist die Kehrseite der geduldigen Variante: Wer die Statusangabe nicht auswertet, übersieht Fehler komplett und arbeitet mit Leerstellen weiter. Der dritte ist eine Begriffsverwechslung: Das Rennen ist kein Werkzeug für Nebenläufigkeit, es liefert nur das erste Ergebnis und verwirft den Rest. Und der vierte ist eine Beruhigung: Die Reihenfolge der Ergebnisse entspricht der Eingabe, nicht der Fertigstellung.

HTTP-Anfragen

4:14 Jetzt holen wir Daten aus dem Netz. Und gleich vorweg der Satz, der in diesem Kapitel am meisten überrascht und für die meisten Fehler in echten Anwendungen sorgt: Ein Statuscode vierhundertvier ist für die Abruffunktion kein Fehler. Die Abruffunktion stellt eine HTTP-Anfrage und liefert ein Promise auf ein Antwortobjekt. Wichtig ist, was dieses Objekt zunächst nicht enthält: die eigentlichen Daten.

4:37 Es trägt Metainformationen — den Statuscode und eine Kurzform davon, die sagt, ob es grundsätzlich geklappt hat. Der Inhalt wird in einem zweiten Schritt geholt, und der ist wiederum asynchron. Sie warten also zweimal: einmal auf die Antwort, einmal auf deren Inhalt. Das wirkt umständlich und ist sinnvoll — bei großen Antworten wollen Sie vielleicht erst den Status sehen, bevor Sie Megabytes einlesen.

5:02 Diese Funktion prüft an drei Stellen, und alle drei sind nötig. Die erste Prüfung ist die überraschende: Die Abruffunktion lehnt nur bei Netzwerkfehlern ab. Ein Statuscode vierhundertvier oder fünfhundert kommt als ganz normale, erfolgreiche Antwort an — der Server hat ja geantwortet, nur eben mit einer Fehlermeldung. Wer diese Prüfung weglässt, versucht anschließend, eine Fehlerseite als Daten zu verarbeiten. Die zweite Wartestelle holt den Inhalt.

5:29 Und die dritte Prüfung ist die, die viele vergessen: Auch gültiges JSON kann eine völlig andere Form haben als erwartet. Diese Haltung begleitet uns seit Modul fünf, hier in ihrer schärfsten Form. Ein erfolgreicher Abruf sagt nichts über die Form der Daten — er sagt nur, dass etwas angekommen ist. Die Umwandlungsfunktion gelingt auch bei fachlich völlig unbrauchbarem Inhalt, solange die Klammern stimmen. Also prüfen Sie die Struktur, bevor Sie damit arbeiten.

5:57 Der vierte Punkt nennt ein Werkzeug, das viele nicht kennen: Für Abbruch und Zeitlimit gibt es einen eigenen Mechanismus, der über ein Signal mit der Anfrage verbunden wird. Ohne ihn kann eine Anfrage theoretisch ewig hängen. Behandeln Sie drei Fehlerfälle getrennt, jeden mit einer eigenen verständlichen Meldung: ein Statuscode vierhundertvier, ein Netzwerkfehler und eine unerwartete Datenstruktur.

6:21 Der Hinweis enthält die Struktur der Lösung: Die drei Fälle entstehen an drei verschiedenen Stellen — vor, bei und nach dem Auslesen. Wenn Sie das einmal sauber getrennt haben, wissen Sie im Fehlerfall sofort, wo Sie suchen müssen. Und der Nutzer bekommt eine Auskunft, die zu seiner Situation passt, statt eines pauschalen „Etwas ist schiefgelaufen".

6:41 Auf einen Fehler bei vierhundertvier zu warten, der nie kommt, ist der Kernfehler dieses Kapitels — Ihr Fangblock bleibt leer, und der Code läuft mit einer Fehlerseite als Nutzdaten weiter. Das Ergebnis des Inhaltsabrufs ohne Warten zu verwenden, liefert Ihnen ein Promise statt der Daten; die Fehlermeldung ist meist verwirrend.

6:59 Die Struktur der Antwort als gegeben anzunehmen, ist der dritte Punkt und in der Praxis eine ergiebige Fehlerquelle, gerade wenn sich fremde Schnittstellen ändern. Und Zeitlimits wegzulassen, führt zu hängenden Anfragen, die niemand bemerkt.

Nacheinander oder gleichzeitig

7:13 Jetzt die Frage, die ich zu Beginn angekündigt habe. Sie ist so einfach, dass man sie leicht übergeht, und sie entscheidet über die Laufzeit Ihrer Anwendung: Hängt dieser Schritt vom vorigen ab? Wir rechnen das gleich vor. Die Unterscheidung ist logisch, nicht technisch. Abhängige Schritte müssen nacheinander laufen — der zweite braucht das Ergebnis des ersten, daran ist nichts zu ändern.

7:37 Unabhängige Schritte dagegen lassen sich gemeinsam starten und gemeinsam abwarten. Und dann sinkt die Gesamtdauer erheblich, weil die Wartezeiten sich überlappen. Das Bild dazu ist die Küche: Wasser aufsetzen und Gemüse schneiden geht gleichzeitig. Nudeln abgießen kann man erst, wenn sie gar sind. Wer alles nacheinander macht, obwohl es nicht nötig ist, braucht dreimal so lange — bei gleichem Aufwand.

8:01 Rechnen wir es vor. Drei Vorgänge zu je einer halben Sekunde. Oben wird jeder einzeln abgewartet — das ergibt anderthalb Sekunden, weil die Wartezeiten sich addieren. Unten werden alle drei gestartet und dann gemeinsam abgewartet: rund eine halbe Sekunde, weil sie sich überlappen. Dreimal schneller, bei identischem Code-Aufwand.

8:21 Das Ergebnis wird per Destrukturierung in drei Variablen entpackt — die Technik aus Modul drei. Und der Fußtext ist die notwendige Warnung: Die obere Fassung ist nicht falsch, sie ist nur dann richtig, wenn ein Schritt tatsächlich das Ergebnis des vorigen braucht. Die Entscheidungskette ist kurz. Braucht Schritt zwei das Ergebnis von Schritt eins — nacheinander, ohne Diskussion. Sind sie unabhängig — gemeinsam starten.

8:47 Und dann kommt die Frage aus dem ersten Kapitel: Muss alles gelingen, oder ist Teilerfolg in Ordnung? Damit fällt die Wahl des Kombinators. Der letzte Punkt weitet den Blick: Diese Frage stellt sich bei Dateien genauso wie bei Netzwerkzugriffen. Drei Dateien lesen, drei Datenbankabfragen, drei Anfragen — überall dasselbe Muster. Es lohnt sich, die Frage zur Gewohnheit zu machen.

9:12 Überführen Sie drei unabhängige Ladevorgänge in die gemeinsame Form — und lassen Sie einen tatsächlich abhängigen Schritt hinten stehen. Diese Mischung ist realistisch: In echtem Code ist selten alles unabhängig oder alles abhängig. Der Hinweis nennt den Beleg, den ich Ihnen empfehle: Messen Sie die Dauer beider Fassungen.

9:30 Eine Zeitmessung um beide Blöcke herum genügt, und der Unterschied steht schwarz auf weiß auf der Konsole. Nichts überzeugt so gut wie eine Zahl, die man selbst gemessen hat. Jeden Aufruf einzeln abzuwarten, obwohl die Aufrufe unabhängig sind, ist die häufigste vermeidbare Langsamkeit in asynchronem Code — und sie fällt in der Entwicklung nicht auf, weil dort alles schnell ist.

9:52 Der umgekehrte Fehler ist schlimmer: Vorgänge zu parallelisieren, die aufeinander aufbauen, liefert falsche Ergebnisse statt langsamer. Die Fehlerbehandlung beim gemeinsamen Abwarten zu vergessen, erzeugt unbehandelte Ablehnungen. Und der letzte Punkt ist eine Frage des Maßes: Sehr viele Vorgänge gleichzeitig zu starten, überlastet die Gegenstelle — hundert parallele Anfragen sind selten eine gute Idee.

Asynchrone Dateizugriffe

10:15 Jetzt zu den Dateien. Technisch ist das schnell erklärt — die Lernpointe liegt woanders: Beim Einlesen einer JSON-Datei gibt es zwei ganz verschiedene Fehlerquellen, und die sollte man auseinanderhalten. Node stellt versprechenbasierte Dateifunktionen bereit — sie liefern Promises und werden mit dem Warteschlüsselwort verwendet, genau wie alles andere in diesem Modul.

10:37 Der entscheidende Satz steht am Ende: Gelesen wird Text. Die Umwandlung in Objekte ist ein eigener Schritt, der separat scheitern kann. Diese Trennung ist keine Umständlichkeit, sondern eine Information: Eine Datei kann vorhanden und lesbar sein und trotzdem Unsinn enthalten. Zwei Zustände, zwei Ursachen, zwei ganz verschiedene Antworten an den Nutzer.

10:58 Schauen Sie sich den Fangblock an — er umfasst zwei Zeilen mit zwei völlig verschiedenen Fehlerquellen. Die erste kann scheitern, weil die Datei fehlt oder nicht lesbar ist. Die zweite kann scheitern, weil der Inhalt kein gültiges JSON ist. Beide landen im selben Fangblock, und die Meldung dort ist entsprechend pauschal.

11:17 Das ist der Ausgangspunkt für die Aufgabe. Immerhin: Die technische Ursache bleibt über die Ursachen-Angabe erhalten, das Muster aus dem letzten Modul. Beachten Sie außerdem die Kodierungsangabe beim Lesen — ohne sie bekommen Sie keinen Text, sondern rohe Bytes. Der praktische Grund steht ganz oben: Eine fehlende Datei ist ein anderer Fall als eine beschädigte. Bei der fehlenden legt man vielleicht eine neue mit Standardinhalt an.

11:43 Bei der beschädigten wäre genau das fatal — man würde vorhandene Daten überschreiben. Der Nutzer braucht also je Fall eine andere Auskunft, und Ihr Programm je Fall eine andere Reaktion. Der dritte Punkt ist die Versöhnung: Über die Ursachen-Angabe bleibt die technische Information erhalten, auch wenn die Meldung fachlich formuliert ist.

12:02 Und der vierte wiederholt die Kernaussage — die Umwandlung ist immer ein eigener Schritt. Erzeugen Sie zwei getrennte Meldungen: eine für die fehlende Datei, eine für ungültigen Inhalt. Und die technische Ursache bleibt in beiden Fällen erhalten. Der Hinweis nennt einen Trick, den man nutzen sollte: Erzeugen Sie den zweiten Fall bewusst, indem Sie Text ohne gültiges JSON in die Datei schreiben.

12:25 Fehlerfälle absichtlich herzustellen, ist eine unterschätzte Technik. Sie werden sonst nämlich nie sicher wissen, ob Ihre Behandlung greift — genau das Argument, das wir beim Testen hatten. Die fehlende Kodierungsangabe liefert Ihnen einen Puffer statt Text, und die Umwandlung scheitert dann mit einer Meldung, die auf etwas ganz anderes hinweist.

12:46 Lese- und Umwandlungsfehler in einer Meldung zusammenzuwerfen, ist der Fall aus der Aufgabe — machbar, aber diagnostisch wertlos. Das vergessene Warten führt dazu, dass Sie mit einem Promise weiterarbeiten; das trifft hier genauso zu wie bei den Netzanfragen. Und der letzte Punkt ist praktisch: Beim Schreiben nicht zu prüfen, ob das Zielverzeichnis existiert, erzeugt einen Fehler, der zunächst wie ein Rechteproblem aussieht.

Ereignisse verarbeiten

13:10 Zum Abschluss ein anderes Denkmodell. Bisher haben wir Abläufe geschrieben, die von Anfang bis Ende durchlaufen. Ereignisgetriebene Programme funktionieren anders: Sie melden, dass etwas geschehen ist — und wer darauf reagiert, ist an anderer Stelle entschieden. Ein Ereignis meldet, dass etwas geschehen ist. Ein Handler ist die Funktion, die darauf reagiert.

13:32 Und der dritte Satz beschreibt den Perspektivwechsel: Ereignisgetriebene Programme registrieren Handler und warten, statt einen Ablauf durchzuziehen. Das Bild dazu ist ein Schwarzes Brett. Sie hängen eine Notiz auf — wer sie liest und was er daraufhin tut, wissen Sie nicht und müssen es nicht wissen. Diese Unwissenheit ist kein Mangel, sondern der eigentliche Gewinn. Sie ist der Grund, warum sich solche Systeme erweitern lassen, ohne bestehende Teile anzufassen.

14:00 Zwei Handler auf dasselbe Ereignis, und beide laufen — in der Reihenfolge ihrer Registrierung. Das ist der Kernpunkt: Der Auslöser unten ruft weder das Protokoll noch den Bericht auf. Er meldet nur, dass ein Beet angelegt wurde. Wer darauf reagiert, ist an anderer Stelle festgelegt. Wollen Sie morgen eine dritte Reaktion — eine Benachrichtigung etwa —, hängen Sie sie dazu, ohne die auslösende Stelle anzufassen.

14:25 Beachten Sie auch, dass das Beet als Argument mitgegeben wird. Ohne diesen Kontext wüssten die Handler nur, dass etwas passiert ist, aber nicht womit. Der erste Punkt ist der Kern: Die auslösende Stelle muss ihre Folgewirkungen nicht kennen. Daraus folgt der zweite — neue Reaktionen kommen hinzu, ohne den Auslöser zu ändern.

14:46 Wer schon einmal eine Funktion gesehen hat, in der über die Jahre acht Aufrufe angewachsen sind, weiß, was das wert ist. Die beiden unteren Punkte zeigen, wie verbreitet das Muster ist: In Node arbeiten Datenströme, Server und Prozesse danach. Im Browser ist es die Grundlage jeder Bedienung — jeder Klick ist ein Ereignis. Sie benutzen dieses Muster also längst, jetzt können Sie es auch selbst bauen.

15:11 Hängen Sie zwei unabhängige Reaktionen an dasselbe Ereignis — und stellen Sie sicher, dass die auslösende Funktion keinen Verweis auf eine von beiden enthält. Dieses Kriterium ist der eigentliche Prüfstein: Wenn dort noch ein Aufruf steht, ist die Entkopplung nicht erreicht. Der Hinweis erinnert an den Kontext: Übergeben Sie die betroffenen Daten als Argument.

15:31 Ein Handler, der nur weiß, dass etwas passiert ist, kann nicht viel tun — er braucht die Information, womit. Der erste Punkt ist der unangenehmste dieses Kapitels: Ein vertippter Ereignisname löst keinen Fehler aus. Es passiert schlicht nichts, und Sie suchen in der falschen Richtung. Deshalb legt man Ereignisnamen in der Praxis als Konstanten ab. Handler zu registrieren und nie zu entfernen, führt bei langlaufenden Programmen zu Speicherlecks.

15:58 Der dritte Punkt ist eine wichtige Erwartungskorrektur: Das Auslösen wartet nicht auf asynchrone Handler — es startet sie und läuft weiter. Und der vierte ist ein Maßhinweis: Ereignisse dort einzusetzen, wo ein direkter Aufruf klarer wäre, macht den Ablauf unnötig schwer nachvollziehbar.

Asynchron beherrscht

16:16 Drei Sätze zum Mitnehmen. Erstens die Auswahlregel bei den Kombinatoren: Sie folgt der fachlichen Frage, wie viele Teilvorgänge gelingen müssen — nicht dem Zufall. Zweitens der Satz, der in diesem Modul für die meiste Überraschung sorgt: Die Abruffunktion lehnt nur bei Netzwerkfehlern ab, deshalb gehört die Statusprüfung immer dazu.

16:35 Und drittens die Leitfrage: Nur unabhängige Schritte gehören parallelisiert — alles andere ist entweder langsam oder falsch. Im nächsten Modul ordnen wir unseren Code: Es geht um Module, Pakete und Projektstruktur.

Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →