Javascript Exceptions: try, catch (e), throw

Javascript try catch throw exeption

try catch (e) throw testet einen Codeblock auf Fehler – nicht auf Syntaxfehler wie onerror –, sondern auf Laufzeitfehler. try catch baut eine Umleitung ein, damit die Ausführung nach dem Fehler weitergeführt werden kann. Damit try catch funktioniert, muss der Code frei von Syntaxfehlern sein.

23-02-02 SITEMAP CSS HTML JS Basis JS Web Tutorial SVG

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.

Flußdiagramm try catch
Flußdiagramm try catch

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
}
catch in try catch throw
Mehre als ein catch in try catch throw

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.