JavaScript/Tutorials/Fehlerbehandlung
Der Sprachumfang von JavaScript erlaubt es mittlerweile, umfangreiche Anwendungen zu erstellen. Dynamische Web-Seiten enthalten teilweise umfangreiche Scripts, die in ihren Abläufen auf etliche Variablen zurückgreifen.
Dabei können sich leicht unkontrollierbare Werte und Zustände im Programmablauf einschleichen, zum Beispiel auch durch nicht einkalkuliertes Anwenderverhalten.
Inhaltsverzeichnis
Fehlerbehandlung mit dem error-Event
Das DOM Event error steht außer zur Behandlung von Ladefehlern bei Bildern und Objekten auch auf dem window
-Objekt zur Behandlung von Laufzeitfehlern in JavaScript zur Verfügung. Bitte beachten Sie, dass eine solche Behandlung von JavaScript-Laufzeitfehlern nur in Ausnahmefällen sinnvoll ist, weil sie zu allgemein ist. Mehr als den Fehler zu protokollieren, eventuell Ressourcen freizugeben und vielleicht noch sicherzustellen, dass für den Scriptablauf gesperrte Oberflächenelemente im DOM wieder freigegeben werden, werden Sie nicht tun können. Eine Fortsetzung des normalen Programmablaufes findet nun ohnehin nicht mehr statt, denn wenn Sie im error-Event angelangt sind, ist die Ausführung des laufenden JavaScript-Stacks abgebrochen worden. Wenn Sie an bestimmten Programmstellen mit Laufzeitfehlern rechnen, oder wenn Sie Programmphasen haben, in denen mit Ressourcen agiert wird die eine Freigabe brauchen, dann verwenden Sie exakt an diesen Stellen besser try
, catch
und finally
. Darauf gehen wir im zweiten Teil des Artikels ein.
Für die genannten allgemeinen Fälle haben Sie zwei Möglichkeiten, um sich auf das Error-Event zu registrieren:
error-Eventhandler mit addEventListener registrieren
// Registriere eine Fehlerbehandlung
window.addEventListener("error", fehlerbehandlung);
function fehlerbehandlung (errorEvent) {
const fehler = "Fehlermeldung:\n" +
errorEvent.message + "\n" +
errorEvent.filename + "(Zeile " + errorEvent.lineno + ")";
zeigeFehler(fehler);
errorEvent.preventDefault();
}
function zeigeFehler(meldung) {
alert(meldung);
}
// Löse einen Laufzeitfehler aus, in dem eine undefinierte Funktion aufgerufen wird:
nichtDa();
Sie übergeben an addEventListener
den Namen des Events, für das Sie eine Behandlung registrieren möchten – hier also "error"
, und ein Funktionsobjekt, das bei Eintreten des Fehlers aufzurufen ist. „Funktionsobjekt“, das klingt kompliziert, es ist aber einfach nur der Name einer anderweit definierten Funktion ohne Anführungszeichen. Jede JavaScript-Funktion ist ein Objekt.
Tritt nun ein Fehler ein, wie beispielsweise oben durch den Aufruf der unbekannten Funktion nichtDa
, wird von JavaScript ein so genanntes Error-Objekt ausgelöst. Wird es nicht anderweit abgefangen (wie unten in try/catch beschrieben wird), löst es auf dem window
-Objekt das error
-Event aus, wodurch die registrierte Behandlungsfunktion aufgerufen wird. Diese Behandlungsfunktion erhält, wie alle mit addEventListener registrierten Funktionen, ein Event
-Objekt als Parameter, das für das error
-Event zum ErrorEvent
-Objekt erweitert ist.
Die Erweiterung besteht aus diesen Eigenschaften:
- message: enthält die Fehlerbeschreibung des Fehlers
- filename: enthält den URI der fehlerverursachenden Datei
- lineno: enthält die Zeile, in der der Fehler auftritt
- colno: (nicht standardisiert) enthält die Spalte, in der der Fehler auftritt
- error: (nicht standardisiert) enthält das [JavaScript/Objekte/Error|Error]-Objekt, das vom System ausgelöst wurde
Am Ende der Behandlungsfunktion haben Sie die Wahl, ob Sie den Fehler auch von der JavaScript-Umgebung melden lassen wollen oder nicht. Standardmäßig wird JavaScript den Fehler in der Konsole der Browser-Entwicklertools ausgeben, manche Browser zeigen auch ein eigenes Popup-Fenster an. Jedes Event-Objekt kennt eine Methode preventDefault
, durch deren Aufruf man (meistens) verhindern kann, dass die Standardaktion des Browsers für dieses Event durchgeführt wird.
Das oben gezeigte Beispiel löst durch den Aufruf einer unbekannten Funktion nichtDa()
einen Referenzfehler aus. Daraufhin wird die zuvor registrierte Behandlung des error
-Ereignisses ausgelöst. Die programmierte Behandlung ist sehr einfach, sie baut mittels Zeichenkettenverknüpfung in der Variablen Fehler
einen Fehlertext zusammen und zeigt ihn mit Hilfe einer weiteren Funktion an. Danach wird mit preventDefault
dafür gesorgt, dass der Browser den Fehler nicht weiter beachtet.
In einer komplexeren Anwendung hätte die Funktion fehlerbehandlung()
also alle Möglichkeiten, auf den Fehler zu reagieren. Sie könnte beispielsweise die Zeichenkette der message
-Eigenschaft im erhaltenen errorEvent
-Objekt untersuchen, um herauszufinden, welcher Fehler genau aufgetreten ist. Daraufhin könnten Oberflächen-Elemente gesperrt oder freigegeben werden, oder der Cursor in ein bestimmtes Formularfeld gesetzt werden. Wie aber eingangs beschrieben, ist der globale Error-Handler dafür eigentlich der falsche Ort.
Ergebnis: Ausgabe eines alerts:
Verwendung von window.onerror
Wie für die meisten anderen Events gibt es auch für error die Möglichkeit, eine Behandlungsfunktion an die onerror Eigenschaft zuzuweisen. Statt window.addEventListener
können Sie auch schreiben:
window.onerror = fehlerbehandlung;
Auch dann wird die Funktion fehlerbehandlung
bei Auftreten eines Laufzeitfehlers aufgerufen. Aus historischen Gründen erhält sie aber andere Parameter als andere mit on-Eigenschaften registrierte Ereignisbehandler.
- Parameter: Die Fehlernachricht (entspricht eventError.message)
- Parameter: Die Datei, in der der Fehler auftrat (entspricht eventError.filename)
- Parameter: Die Zeilennummer, in der der Fehler auftrat (entspricht eventError.lineno)
Das folgende Beispiel zeigt, wie die Funktion zur Fehlerbehandlung dann aussehen muss. Weil es kein Event-Objekt gibt, auf dem man preventDefault
aufrufen könnte, erwartet der Browser hier statt dessen die Rückgabe von true
oder false
. true
bewirkt, dass der Browser den Fehler nicht beachtet.
Der Inhalt enthält ein Gleichheitszeichen, das den Parser durcheinanderbringt. Bitte ein 1= am Anfang einfügen: {{BeispielCode|1= ...}}
(Es kann aber auch sein, dass kein Inhalt vorhanden ist.)
Fehlerbehandlung mit try..catch
Mit Hilfe der Fehlerbehandlung mit dem error
-Event-Handler können Sie in einem JavaScript Fehler abfangen, nachdem diese aufgetreten sind. Mit der hier vorgestellten Methode können Sie jedoch bereits im Vorfeld verhindern, dass in kritischen Situationen überhaupt Fehler auftreten. Dazu steht seit der JavaScript-Version 1.5 das try..catch
-Statement zur Verfügung. Es erlaubt Ihnen, Variablen und Werte zu überprüfen und je nach Ergebnis zu reagieren.
Das Beispiel ist so konstruiert, dass es auf eine Variable zugreift, die zunächst noch gar nicht existiert. Das würde zu einem Fehler führen. Das Script "weiß" jedoch, dass dieser Fehler auftreten kann und überprüft deshalb mit dem try..catch
-Statement laufend, ob die Variable schon existiert. Erst wenn die Variable existiert, wird die Überwachung beendet.
setTimeout("x=3", 200);
function zeigeErgebnis (Zaehler, Ergebnis) {
alert("Nach " + (Zaehler) + " Durchläufen existierte x.\nDie Zahl x ist " + Ergebnis + ".")
}
function teste_x (Zaehler) {
try {
if (x == 2) {
throw "richtig";
} else if (x == 3) {
throw "falsch";
}
} catch (e) {
if (e == "richtig") {
zeigeErgebnis(Zaehler, e);
return;
} else if (e == "falsch") {
zeigeErgebnis(Zaehler, e);
return;
}
} finally {
Zaehler++;
}
setTimeout("teste_x(" + Zaehler + ")", 30);
}
teste_x(0);
Am Ende der Datei ist ein Script-Bereich notiert. Darin werden die erste und die letzte Anweisung sofort beim Einlesen ausgeführt. Die restlichen Anweisungen stehen in Funktionen.
Die Variable x
Während des Ladens der Datei wird eine Variable x
zeitverzögert mit dem Wert 3 belegt. Dazu dient die Methode setTimeout()
. Die Variable ist also erst nach 200 Millisekunden verfügbar. Jeder Versuch, vorher auf diese Variable zuzugreifen, würde zu einem Fehler führen.
Die nächste Anweisung, die im Beispiel direkt ausgeführt wird, ist die letzte im Script-Bereich, nämlich teste_x(0)
. Damit wird die Funktion teste_x()
aufgerufen, die oberhalb notiert ist.
Die Funktion teste_x()
Diese Funktion versucht, auf die Variable x
zugreifen. Da zum Zeitpunkt des Aufrufes der Funktion jedoch noch nicht sicher ist, ob die Variable x
bereits existiert, ist innerhalb der Funktion zur Vermeidung von Fehlermeldungen das try..catch
-Statement notiert.
Beim Aufruf erhält die Funktion teste_x()
einen Parameter namens Zaehler
übergeben. Das dient im Beispiel zu Kontrollzwecken.
Aufbau des try..catch-Statements
Das try..catch
-Statement hat generell folgenden Aufbau: Nach dem Schlüsselwort try (try = versuchen) wird eine öffnende geschweifte Klammer, gefolgt von der zu prüfenden Bedingung, notiert. Je nach Erfordernissen können Sie dann mit throw
(throw = auswerfen) eigene Fehler definieren. Die throw
-Definition ist jedoch optional. Anschließend folgt eine schließende geschweifte Klammer, die den try-Block beendet.
Daran anschließend notieren Sie das Schlüsselwort catch
(catch = abfangen). catch
hat Funktions-Charakter und erwartet einen Parameter e
. Die Variable e
ist erforderlich, da Sie über diese Variable letztlich die Reaktion des Scripts auf den aktuellen Zustand kontrollieren. Den Namen der Variablen können Sie frei wählen (der Name muss also nicht e
sein). Innerhalb des Funktionsblocks von catch()
, der wie üblich in geschweiften Klammern steht, können Sie die mit throw
definierten Fehler auswerten und darauf reagieren.
Zuletzt können Sie noch das Schlüsselwort finally
notieren. In dem davon abhängigen Anweisungsblock können Sie weitere Anweisungen notieren. Diese Anweisungen werden unabhängig von der Fehlerbehandlung in jedem Fall ausgeführt.
Anwendung des try..catch-Statements im Beispiel
Im ersten Teil der Anweisung wird geprüft, ob die Variable x
den Wert 2 oder 3 besitzt. Je nach Ergebnis werden mit throw
verschiedene Fehlerwerte definiert. Hat x
den Wert 2, so wird der "Fehler" mit dem Wert richtig generiert. Hat sie den Wert 3 so erhält der Fehler den Wert falsch. Weitere Fehlervarianten werden nicht behandelt.
Im ersten Durchlauf existiert die Variable x
im Beispiel noch gar nicht, da sie ja erst nach 200 Millisekunden existiert. Sie kann damit weder den Wert 2 noch den Wert 3 besitzen. In der nachfolgenden Fehlerbehandlungsroutine catch(e)
wird geprüft, ob einer der definierten Fehler, also richtig oder falsch, aufgetreten ist. Zunächst ist das offensichtlich nicht der Fall. Die Anweisungen, die von den "Fehlerwerten" richtig und falsch abhängig sind, werden deshalb nicht ausgeführt. Die finally
-Anweisung wird dagegen in jedem Fall ausgeführt. Sie bewirkt im Beispiel, dass der übergebene Parameter Zaehler
um 1 erhöht wird.
Gesamtkontrolle
Am Ende ruft sich die Funktion teste_x()
mit setTimeout()
um 30 Millisekunden zeitverzögert selbst wieder auf. So behält sie die Kontrolle über das Geschehen, bis ein definierter Zustand eintritt. Der Parameter Zaehler
wird dabei mit Hilfe einer Zeichenkettenverknüpfung übergeben.
Interessant wird es, wenn der Zeitpunkt erreicht ist, zu dem die Variable x
existiert. In diesem Fall tritt einer der vordefinierten Fälle ein. Da x
den Wert 3 besitzt, wird der throw
-Fehler mit dem Wert falsch generiert (dies soll im Beispiel einfach zeigen, dass throw
zur Erzeugung von Werten gedacht ist, die durchaus und oft auch Fehlerzustände bezeichnen). Im nachfolgenden catch(e)
-Block führt dies dazu, dass die Funktion zeigeErgebnis()
aufgerufen wird. Im Beispiel wird für beide definierten throw-Werte die gleiche Funktion aufgerufen. Sie können an dieser Stelle jedoch auch völlig verschiedene Anweisungen notieren. Jede dieser Fehlerbehandlungsroutinen bricht gleichzeitig die Funktion teste_x()
mit return ab, da x
ja nun existiert und der "kritische Zustand" beendet ist.
Ausgabe des Ergebnisses
Die Funktion zeigeErgebnis()
erhält als Parameter die Variablen Zaehler
und Ergebnis
übergeben. In der Variablen Zaehler
ist die Anzahl der Durchläufe bis zur Existenz der Variablen x
gespeichert und in der Variablen Ergebnis das Resultat der Fehlerbehandlung. Mit alert()
wird im Beispiel zur Kontrolle ausgegeben, wie viele Durchläufe benötigt wurden und was für ein Ergebnis erreicht wurde.
Anwendungsfälle
Prüfungen mit dem try..catch-Statement sind z. B. dann sinnvoll, wenn Sie wie im Beispiel mit setTimeout()
zeitversetzte Aktionen ausführen und davon abhängige Anweisungen ausführen wollen. Ebenfalls sinnvoll ist das Statement, wenn Sie z. B. auf Variablen oder Funktionen zugreifen wollen, die in anderen Frame-Fenstern notiert sind, wobei das Script nicht wissen kann, ob die Datei im anderen Frame-Fenster, in der das entsprechende Script notiert ist, bereits eingelesen oder überhaupt die dort aktuell angezeigte Seite ist.
Siehe auch
Heute kann man Skripte bequem mit der Konsole überprüfen und mit Haltepunkten das Skript solange ablaufen lassen, bis es einen Fehler meldet: