Fehlerursachen
Fehler entstehen durch fehlerhafte Anweisungen, durch fehlende oder falsche Eingaben, die miesen kleinen Fallen lauern in unvorhergesehenen Umständen und Ereignissen. Die meisten Fehler entstehen bei der Verarbeitung von Benutzereingaben und Daten von externen Ressourcen, denn das sind die unzuverlässigsten Quellen.
Klassiker für das Abfangen unvorhergesehener Ereignisse sind der XHR-Request oder fetch – wenn z.B. die abgerufenen Daten nicht zur Verfügung stehen:
fetch("noppet.json") .then((response) => response.json()) .then((data) => { document.querySelector(".result").innerHTML = "Hallo User" }) .catch((error) => { console.log(error); document.querySelector(".result").innerHTML = error; });
Anstelle einer Fehlermeldung in der Konsole wie SyntaxError: The string did not match the expected pattern, die der Benutzer gar nicht erst zu Gesicht bekommt, können wir ihn mit einer sinnvollen Fehlermeldung informieren.
Im Internet sind alte Browsergenerationen mit ihren Bugs, Mankos und veralteten Versionen unterwegs, die Verbindung zu einem externen Server kann unterbrochen werden. Einige Fehler sind sofort sichtbar, andere können für Jahre unter der Oberfläche schmoren, bis eine unvorhergesehene Benutzeraktion die fehlende Abfrage triggert.
Fehler gibt es immer wieder
Sobald ein Programm einen gewissen Grad an Komplexität erreicht, sind solche Situationen unvermeidlich. Sie müssen so gut wie möglich vorhergesehen und vorausschauend behandelt werden.
Exceptions – Ausnahme-Zustände
Programmiersprachen wie C oder C++ bieten schon lange Exceptions. Eine Exception – Ausnahme-Situation – entsteht bei einem Laufzeitfehler aufgrund einer illegalen Aktion, also wenn die Syntax korrekt ist. Fehler, der nicht von einer try-catch-Anweisung abgefangen werden, erzeugen ein onerror-Event auf dem Window-Objekt.
Das klassische Beispiel ist der Zugriff auf eine undefinierte Variable oder der Aufruf einer Methode, die es nicht gibt.try { console.log (x); } catch (error) { console.log ("Fehler:", error) }
Fehler: – ReferenceError: Can't find variable: x
Javascript hat seit Version 4.1 den try-catch()-Block mit dem »Werfen von Ausnahmen« Throw Exception. Jetzt sind wir also nicht mehr den Browsern ausgeliefert, die uns ihre Fehlermeldungen vor die Füße werfen und dann aussteigen, sondern können eine Umleitung einbauen.
Bei größeren Programmen überwiegt das Error Handling – die Behandlung von Fehlern und Ausnahmen – schnell die Logik des Programms. try … catch () … throw behandelt Fehler an zentraler Stelle und gestaltet den Code lesbarer.
- try {}
- testet einen Codeblock auf Fehler
- catch {}
- reagiert auf den Fehler und erledigt die Fehlerbehandlung
- throw {}
- erzeugt eigene Fehlermeldungen
- finally {}
- führt Anweisungen im Anschluss an try und catch aus, ob nun Fehler aufgetreten sind oder nicht und egal wie das Ergebnis aussah.
try catch: Exception-Handling
Exceptions (Ausnahmen) treten auf
- wenn ein Fehler zur Laufzeit auftritt, z.B.
- beim Aufruf einer undefinierten Variablen,
- beim Aufruf einer Methode, die es gar nicht gibt (die der Browser nicht unterstützt),
- bei einer falsche Benutzereingabe
- wenn das Script selber eine Exception wirft – throws an exception.
Ein Klassiker: Der Zugriff mit querySelector auf ein Element, das es nicht gibt.
<script> let brd = document.querySelector(".brd") !== null; try { if (brd) { // Wird nie erreicht res.textContent = "wird gemacht"; } else { // Eine eigene Fehlermeldung erzeugen throw new Error ("Kein Element mit class brd gefunden"); } } catch (e) { // Fehler entdeckt console.log(e); } finally { console.log("Hier gehts auf jeden Fall weiter"); } </script>
[Log] Error: Kein Element mit class brd gefunden [Log] Hier gehts auf jeden Fall weiter
Das Element document.querySelector(".brd") gibt es nicht, aber es entsteht keine »natürliche« Fehlermeldung in der Konsole. Stattdessen erzeugt throw die eigene Ausnahme (Exeption): als String, Number, Boolean oder als Objekt.
Solche Fehler sollten während der Entwicklung abgefangen werden. Wenn dem Benutzer eine Fehleingabe mitgeteilt werden soll, ist eine eigene Formulierung besser als der Text einer Fehlermeldung der Konsole.
function mwst (amount) { if (isNaN (amount)) { throw new Error (amount + " ist kein korrekter Betrag"); } return amount * 19 / 100; } try { const brutto = mwst ("holla"); document.querySelector(".amount").innerText = brutto; } catch (error) { document.querySelector (".userError").innerText = error }
Der normale Code sitzt in den Klammern einer try-Anweisung. Die catch (e)-Anweisung bindet die Anweisungen ein, die im Fall einer Ausnahme ausgeführt werden.
catch error – error.stack – Fang die Fehlermeldung ab
Der Parameter error gibt den Fehler aus. Aber wie kommen wir an die Zeile, die zur Fehlermeldung geführt hat? Man könnte ja try – catch mal kurz weglassen, aber das wäre keine gute Idee.
catch (error) { // Fehler entdeckt console.log(error + " stack " + error.stack + " line " + error.line + " lineNumber " + error.lineNumber); }
Error: Kein Element mit class brd gefunden stack global code@myscript:238:18 line 238 lineNumber undefined
Nicht alle Browser unterstützen den Fehlerstack. Firefox reagiert nicht auf error.line, nur auf error.lineNumber.
Der Code im finally-Block wird immer ausgeführt, gleich, ob eine Exception – ein Fehler – auftrat oder nicht.
Trotz des Fehlers wird das Script dann an einer (hoffentlich) sicheren Stelle weiter ausgeführt.
throw
Browser haben viele Fehlermeldung fertig auf Lager, die aber nicht direkt die Fehlerquelle aufdecken. Scripte können ihre eigenen Exceptions mit der Anweisung throw werfen. Die Exception kann ein String, eine Zahl oder ein Objekt sein.
- throw "Hier ist ein Fehler";
- throw 12
- throw new Error ("Hier ist ein Fehler")
Wenn wie hier im Beispiel die Anwendung auf dem Server einen Wert nicht zurückgibt, der erwartet wird:
let json = '{"price": 675}'; try { let product = JSON.parse (json); if (!product.name) { throw new Error("Unvollständige Daten: Keine Produktnummer"); } console.log ( product.name); } catch (e) { console.log ("JSON : " + e); }
JSON : Error: Unvollständige Daten: Keine Produktnummer
So kommt Javascript mit einer Fehlermeldung, die ausdrucksvoller ist als das übliche Reference Error des Browsers.
Muster von try catch throw
try { // Anweisungen, die zu Ausnahmen führen könnten } catch ( exception1 ex ) { // Anweisungen, die diese Ausnahme behandlen } catch ( exception2 ex ) { // Anweisungen, die diese Ausnahme behandlen } catch ( exception3 ex ) { // Anweisungen, die diese Ausnahme behandlen }
try-catch()-throw oder if-than-else
Für Fehler, auf die das Script vorbereitet ist, die wir voraussehen, die häufiger eintreten könnten und von denen sich ein Script erholen kann, wählt man i.d.R. eher einer if-than-else-Behandlung.
Mit if than else wird aber immer abgefragt, ob der Fehler nun auftritt oder nicht. Wenn der Fehler unter normalen Bedingungen nur selten auftritt, ist Exception-Handling effizienter, weil das Script unter normalen Bedingungen keine weiteren Prüfungen abhandelt.
Für gravierende Fehler, die eine Ausnahme darstellen, ist try catch () throw gedacht. Das schafft zwei Ebenen von Fehlermeldungen.