Zum Inhalt springen

Aufgabe 03-3a - Promise-Fehlerjagd

Zu Zen-Modus wechseln
  • Du erkennst typische Async-Fehler (vergessenes await, nie gefangene Ablehnung, try ohne await, forEach mit async-Funktion, catch ohne Rückgabe) in fremdem Code an ihren Symptomen.
  • Du behebst sie begründet statt durch Herumprobieren und erklärst den Ablauf mit dem Event-Loop-Modell.
  • Du sicherst eigenen asynchronen Code mit einer Checkliste systematisch ab.
  • Du setzt die Biome-Regel noFloatingPromises ein und beurteilst, welche Fehler ein Linter findet und welche nicht.

Eine Ärztin behandelt nicht auf Verdacht, sondern stellt zuerst eine Diagnose: Symptom beobachten, Ursache finden, gezielt behandeln. Genau so gehst du hier mit asynchronen Fehlern um, statt so lange herumzuprobieren, bis es “irgendwie geht”.

Fehlerbehandlung in asynchronem Code ist die häufigste Stolperstelle des Jahres. Du jagst die klassischen Async-Fehlerbilder in fünf vorbereiteten Programmen, den Jagdrevieren revier1.js, revier2.js, revier3.js, revier4.js und revier5.js. Jedes hat genau einen eingebauten Fehler und läuft mit node revierN.js. Danach härtest du deine Pizzeria aus Aufgabe 03-2a ab (siehe Kapitel Asynchrones JavaScript, Einheiten 3.2 und 3.3, jeweils die Abschnitte zu den Fallen und die Stolperfallen).

In den fünf Revieren steckt jedes der folgenden Fehlerbilder genau einmal. Welches in welchem Revier steckt, findest du selbst heraus. Die Liste ist zugleich deine Fünf-Punkte-Checkliste:

  • Ein .catch() fängt den Fehler, gibt aber nichts zurück; der Folge-Code rechnet mit undefined weiter.
  • forEach mit einer async-Funktion: Die Reihenfolge stimmt nicht, und niemand wartet auf das Ende.
  • Ein await fehlt; weiterverarbeitet wird das Promise statt des Werts.
  • try/catch steht um den Aufruf einer Funktion, die ein Promise liefert, aber ohne await; der Fehler fliegt am catch vorbei.
  • Ein abgelehntes Promise wird nie gefangen; das Programm stirbt mit einer Unhandled Rejection.

Projektordner anlegen siehe https://qiz.at/projektordner

  • Lies vorher kurz https://qiz.at/assignmentsabgabe durch.
  • Nur vollständige Abgaben (siehe https://qiz.at/rules) können positiv bewertet werden.
  • Abgabe- und Testdetails siehe https://qiz.at/assignmentsabgabe
  • Die Übung liegt als Unterordner uebung-03-3a in deinem Jahres-Repository SJ26_27_4AHITM_SEW_<Benutzername>_exercises. Darin liegen die reparierten Reviere als revierN-fixed.js und eine Datei fehlerjagd.md mit Symptomen, Ursachen und Erklärungen. Die Änderungen an der Pizzeria aus Aufgabe 03-2a committest du dort als eigenen Commit. Du arbeitest allein.
  • Führ die fünf Reviere zunächst nur aus und notiere pro Revier das Symptom in einem Satz (was ist an der Ausgabe falsch oder verdächtig?), noch ohne in den Code zu sehen.
  • Für die Reviere 1, 2 und 3: Ordne jedem sein Fehlerbild aus der Liste zu, nenne die Ursache in einem Satz, behebe sie mit einer kleinen Änderung in revierN-fixed.js und notiere die neue Ausgabe.
  • Beim Revier mit try/catch: Erklär schriftlich, warum der catch-Block nie läuft. Wann verlässt die Ausführung den try-Block, wann scheitert das Promise?

Tipp: Zuerst das Symptom, dann der Code: Wer sofort in den Code schaut, trainiert nicht das Erkennen. [object Promise] oder Promise { <pending> } in einer Ausgabe heißt fast immer: Hier fehlt ein await. Der Stacktrace einer Unhandled Rejection zeigt die Stelle, an der der Fehler geworfen wurde, nicht die, an der er hätte gefangen werden müssen. Den Exit-Code zeigt in der Git Bash echo $?, in PowerShell $LASTEXITCODE; 0 heißt “alles in Ordnung”.

Vollständig, wenn:

  • für alle fünf Reviere das Symptom vor dem Codeblick notiert ist.
  • den Revieren 1 bis 3 das richtige Fehlerbild zugeordnet ist, die Ursache korrekt benannt ist und die reparierten Fassungen ohne Absturz das richtige Ergebnis liefern.
  • die Erklärung zu try/catch ohne await den zeitlichen Ablauf benennt (Block verlassen, Promise scheitert später).

Wie unter Allgemein.

Für alle, die in der Einheit mit den Schritten oben fertig sind bzw. optional (= freiwillig), wenn du in der Einheit nicht fertig wirst.

Erweitere (siehe Erweitern in https://qiz.at/rules) den vollständigen Teil der vorherigen Stufe dieser Datei (ändere oder lösche nur, was für diese Aufgabe unbedingt notwendig ist):

  • Für die Reviere 4 und 5 genauso: Fehlerbild, Ursache, kleine Behebung, neue Ausgabe.
  • Beim Revier mit forEach: Bau es so um, dass die Bestellungen wirklich der Reihe nach zubereitet werden und “Alle Bestellungen bearbeitet” erst danach erscheint. Erklär den Unterschied zur Fassung mit forEach.
  • Geh deine Pizzeria aus Aufgabe 03-2a mit der Fünf-Punkte-Checkliste durch: Behebe, was du findest, und notiere für jeden der fünf Punkte, wo du nachgesehen hast.

Tipp: forEach ruft die Funktion für jedes Element auf und wartet nie auf ihr Promise; welche Schleife wartet, zeigt Einheit 3.3 im Abschnitt “await in Schleifen”. Ein catch ist auch eine Rückgabe: Was es zurückgibt, ist der neue Wert der Kette.

Vollständig, wenn:

  • den Revieren 4 und 5 das richtige Fehlerbild zugeordnet ist und beide danach korrekt laufen.
  • das forEach-Revier die Bestellungen der Reihe nach abarbeitet und der Unterschied erklärt ist.
  • die Pizzeria nachweislich mit allen fünf Punkten der Checkliste geprüft ist und Funde behoben sind.

Wie unter Allgemein.

Für alle, die in der Einheit mit den Schritten oben fertig sind bzw. optional (= freiwillig), wenn du in der Einheit nicht fertig wirst.

Erweitere (siehe Erweitern in https://qiz.at/rules) den vollständigen Teil der vorherigen Stufe dieser Datei (ändere oder lösche nur, was für diese Aufgabe unbedingt notwendig ist):

  • Verankere eine Regel in der Pizzeria: Jede async-Funktion, die das Hauptprogramm aufruft, behandelt Fehler entweder selbst oder dokumentiert im Kommentar, dass der Aufrufer fangen muss. Kein stilles Verschlucken, nirgends.
  • Konstruiere ein kleines Programm mit einem Floating Promise: Eine async-Funktion wird aufgerufen, ihr Ergebnis ignoriert, und sie scheitert erst nach einer kurzen Wartezeit. Dokumentiere zwei Fälle: ohne weitere Maßnahmen (In welcher Reihenfolge erscheinen deine “fertig”-Meldung und der Fehler? Zeigt die Fehlermeldung, wo das await fehlt?) und mit einem globalen Handler, wie ihn manche Frameworks setzen: process.on("unhandledRejection", () => {}); ganz oben. Weise nach, dass der Fehler dann spurlos verschwindet (etwa über ein leeres Fehlerprotokoll und den Exit-Code).
  • Zeig zwei Gegenmittel: das await mit try/catch und, für bewusst nebenläufige Aufrufe, ein angehängtes .catch() mit Protokollierung.

Tipp: Halte die Wartezeit kurz (10 bis 50 ms) und gib nach dem Aufruf sofort “fertig” aus, dann siehst du die Reihenfolge deutlich.

Vollständig, wenn:

  • die Regel an jeder betroffenen Funktion umgesetzt ist.
  • beide Fälle mit Ausgabe und Exit-Code dokumentiert und analysiert sind.
  • beide Gegenmittel gezeigt sind und den Fehler sichtbar machen.

Wie unter Allgemein.

Für alle, die in der Einheit mit den Schritten oben fertig sind bzw. optional (= freiwillig), wenn du in der Einheit nicht fertig wirst.

Erweitere (siehe Erweitern in https://qiz.at/rules) den vollständigen Teil der vorherigen Stufe dieser Datei (ändere oder lösche nur, was für diese Aufgabe unbedingt notwendig ist):

  • Lass Biome nach solchen Fehlern suchen. Leg die fünf Original-Reviere, deine reparierten Fassungen und dein Programm aus der vorigen Stufe in uebung-03-3a (mit "type": "module" in der package.json), installiere Biome mit npm install --save-dev --save-exact @biomejs/biome und erzeuge mit npx @biomejs/biome init eine biome.json.
  • Führ npx @biomejs/biome lint . aus. Meldet Biome deine Floating Promises? Schalte dann die Regel noFloatingPromises ein (Gruppe nursery, unter linter.rules der Eintrag "nursery": { "noFloatingPromises": "error" }) und führ den Befehl erneut aus.
  • Beurteile die Meldungen: Welche der fünf Fehler findet die Regel, welche nicht, und warum? Warum meldet sie in jedem Original-Revier auch den Aufruf in der letzten Zeile, oft sogar in deinen reparierten Fassungen? Wie bringst du diese Meldung begründet zum Verschwinden? Lies dazu die Biome-Dokumentation zu noFloatingPromises.
  • Schreib den Merkzettel “Async-Fehler: die Klassiker” für deine Klasse: je Fehlerbild eine Zeile Symptom und eine Zeile Gegenmittel, höchstens eine halbe Seite.

Tipp: Biome meldet auch anderes, zum Beispiel unbenutzte Parameter. Für diese Stufe zählen nur die Meldungen mit lint/nursery/noFloatingPromises. Ein await auf oberster Ebene braucht "type": "module"; npm init -y trägt "commonjs" ein.

Vollständig, wenn:

  • die Regel eingeschaltet ist und ihre Funde mit den fünf Revieren verglichen sind.
  • die Meldung zum letzten Aufruf erklärt und begründet behoben ist.
  • der Merkzettel knapp und vollständig ist.

Wie unter Allgemein.

  1. Woran erkennst du in einer Ausgabe ein vergessenes await?
  2. Warum fängt ein try/catch ohne await den Fehler einer async-Funktion nicht?
  3. Was ist ein Floating Promise, und warum ist es gefährlich, auch wenn Node.js das Programm beim Fehler beendet?
  4. Warum ist forEach mit async-Callbacks eine Falle, und was ist der Ersatz?
  5. Was muss ein .catch() tun, damit der Folge-Code nicht mit undefined weiterrechnet?

Durchschnittlich bis Wesentlich_vollständig: ca. 100 min

  • Oliver Maurhart (MAO)
  • Michael Gabriel (GAM)

CC-BY-SA

https://qiz.at/rules