Egal wie gut wir im Programmieren sind, manchmal enthalten unsere Skripte Fehler. Sie können aufgrund unserer Fehler, einer unerwarteten Benutzereingabe, einer fehlerhaften Serverantwort und aus tausend anderen Gründen auftreten.
Normalerweise „stirbt“ ein Skript im Falle eines Fehlers (wird sofort gestoppt) und gibt es auf der Konsole aus.
Aber es gibt ein Syntaxkonstrukt try...catch
, mit dem wir Fehler „abfangen“ können, damit das Skript etwas Vernünftigeres tun kann, anstatt abzusterben.
Das try...catch
Konstrukt besteht aus zwei Hauptblöcken: try
und dann catch
:
versuchen { // Code... } Catch (Err) { // Fehlerbehandlung }
Es funktioniert so:
Zunächst wird der Code in try {...}
ausgeführt.
Wenn keine Fehler aufgetreten sind, wird catch (err)
ignoriert: Die Ausführung erreicht das Ende von try
und fährt fort, wobei catch
übersprungen wird.
Wenn ein Fehler auftritt, wird die try
Ausführung gestoppt und die Steuerung geht an den Anfang von catch (err)
. Die err
Variable (wir können dafür einen beliebigen Namen verwenden) enthält ein Fehlerobjekt mit Details darüber, was passiert ist.
Ein Fehler im try {...}
Block führt also nicht zum Abbruch des Skripts – wir haben die Möglichkeit, ihn in catch
zu beheben.
Schauen wir uns einige Beispiele an.
Ein fehlerfreies Beispiel: zeigt alert
(1)
und (2)
:
versuchen { alarm('Beginn der Testläufe'); // (1) <-- // ...keine Fehler hier alarm('Ende der Versuchsläufe'); // (2) <-- } Catch (Err) { Alert('Catch wird ignoriert, da keine Fehler vorliegen'); // (3) }
Ein Beispiel mit einem Fehler: zeigt (1)
und (3)
:
versuchen { alarm('Beginn der Testläufe'); // (1) <-- lalala; // Fehler, Variable ist nicht definiert! Alert('Ende des Versuchs (nie erreicht)'); // (2) } Catch (Err) { alarm(`Es ist ein Fehler aufgetreten!`); // (3) <-- }
try...catch
funktioniert nur bei Laufzeitfehlern
Damit try...catch
funktioniert, muss der Code ausführbar sein. Mit anderen Worten, es sollte gültiges JavaScript sein.
Es funktioniert nicht, wenn der Code syntaktisch falsch ist, beispielsweise nicht übereinstimmende geschweifte Klammern enthält:
versuchen { {{{{{{{{{{{{ } Catch (Err) { alarm("Die Engine kann diesen Code nicht verstehen, er ist ungültig"); }
Die JavaScript-Engine liest zunächst den Code und führt ihn dann aus. Die Fehler, die in der Lesephase auftreten, werden „Analysezeitfehler“ genannt und können (aus dem Code heraus) nicht behoben werden. Das liegt daran, dass die Engine den Code nicht verstehen kann.
try...catch
kann nur Fehler verarbeiten, die in gültigem Code auftreten. Solche Fehler werden „Laufzeitfehler“ oder manchmal auch „Ausnahmen“ genannt.
try...catch
funktioniert synchron
Wenn eine Ausnahme in „geplantem“ Code auftritt, wie etwa in setTimeout
, dann try...catch
sie nicht ab:
versuchen { setTimeout(function() { noSuchVariable; // Skript wird hier sterben }, 1000); } Catch (Err) { alarm( "funktioniert nicht" ); }
Das liegt daran, dass die Funktion selbst später ausgeführt wird, wenn die Engine das try...catch
-Konstrukt bereits verlassen hat.
Um eine Ausnahme innerhalb einer geplanten Funktion abzufangen, muss try...catch
innerhalb dieser Funktion sein:
setTimeout(function() { versuchen { noSuchVariable; // try...catch behandelt den Fehler! } fangen { alarm( "Hier ist ein Fehler aufgetreten!" ); } }, 1000);
Wenn ein Fehler auftritt, generiert JavaScript ein Objekt, das die Details dazu enthält. Das Objekt wird dann als Argument an catch
übergeben:
versuchen { // ... } Catch (err) { // <-- das „Fehlerobjekt“, könnte ein anderes Wort anstelle von „err“ verwenden // ... }
Für alle integrierten Fehler verfügt das Fehlerobjekt über zwei Haupteigenschaften:
name
Fehlername. Zum Beispiel für eine undefinierte Variable, die "ReferenceError"
ist.
message
Textnachricht mit Fehlerdetails.
In den meisten Umgebungen sind weitere nicht standardmäßige Eigenschaften verfügbar. Eine der am häufigsten verwendeten und unterstützten ist:
stack
Aktueller Aufrufstapel: eine Zeichenfolge mit Informationen über die Reihenfolge der verschachtelten Aufrufe, die zum Fehler geführt haben. Wird für Debugging-Zwecke verwendet.
Zum Beispiel:
versuchen { lalala; // Fehler, Variable ist nicht definiert! } Catch (Err) { alarm(err.name); // ReferenceError alarm(err.message); // lalala ist nicht definiert alarm(err.stack); // ReferenceError: lalala ist nicht definiert bei (...call stack) // Kann auch einen Fehler als Ganzes anzeigen // Der Fehler wird als „Name: Nachricht“ in einen String umgewandelt. alarm(err); // ReferenceError: lalala ist nicht definiert }
Eine neue Ergänzung
Dies ist eine neue Ergänzung der Sprache. Alte Browser benötigen möglicherweise Polyfills.
Wenn wir keine Fehlerdetails benötigen, kann catch
diese weglassen:
versuchen { // ... } Catch { // <-- without (err) // ... }
Lassen Sie uns einen realen Anwendungsfall von try...catch
untersuchen.
Wie wir bereits wissen, unterstützt JavaScript die Methode JSON.parse(str) zum Lesen von JSON-codierten Werten.
Normalerweise wird es zum Dekodieren von Daten verwendet, die über das Netzwerk, vom Server oder einer anderen Quelle empfangen werden.
Wir empfangen es und rufen JSON.parse
wie folgt auf:
let json = '{"name": "John", "age": 30}'; // Daten vom Server let user = JSON.parse(json); // Konvertieren Sie die Textdarstellung in ein JS-Objekt // Jetzt ist der Benutzer ein Objekt mit Eigenschaften aus der Zeichenfolge alarm( user.name ); // John alarm( user.age ); // 30
Ausführlichere Informationen zu JSON finden Sie im Kapitel JSON-Methoden, toJSON.
Wenn json
fehlerhaft ist, generiert JSON.parse
einen Fehler, sodass das Skript „stirbt“.
Sollen wir damit zufrieden sein? Natürlich nicht!
Auf diese Weise erfährt der Besucher nie, dass mit den Daten etwas nicht stimmt (es sei denn, er öffnet die Entwicklerkonsole). Und die Leute mögen es wirklich nicht, wenn etwas „einfach stirbt“, ohne dass eine Fehlermeldung erscheint.
Lassen Sie uns try...catch
verwenden, um den Fehler zu behandeln:
let json = "{ bad json }"; versuchen { let user = JSON.parse(json); // <-- wenn ein Fehler auftritt... alarm( user.name ); // funktioniert nicht } Catch (Err) { // ...die Ausführung springt hierher Alert( „Es tut uns leid, die Daten sind fehlerhaft, wir werden versuchen, sie noch einmal anzufordern.“ ); alarm( err.name ); alarm( err.message ); }
Hier verwenden wir den catch
-Block nur, um die Nachricht anzuzeigen, aber wir können noch viel mehr tun: eine neue Netzwerkanfrage senden, dem Besucher eine Alternative vorschlagen, Informationen über den Fehler an eine Protokollierungseinrichtung senden, … . Alles viel besser als nur zu sterben.
Was ist, wenn json
syntaktisch korrekt ist, aber keine erforderliche name
hat?
So was:
let json = '{ "age": 30 }'; // unvollständige Daten versuchen { let user = JSON.parse(json); // <-- keine Fehler alarm( user.name ); // kein Name! } Catch (Err) { alarm( "wird nicht ausgeführt" ); }
Hier läuft JSON.parse
normal, aber das Fehlen des name
ist für uns eigentlich ein Fehler.
Um die Fehlerbehandlung zu vereinheitlichen, verwenden wir den throw
-Operator.
Der throw
-Operator generiert einen Fehler.
Die Syntax lautet:
wirf <Fehlerobjekt>
Technisch gesehen können wir alles als Fehlerobjekt verwenden. Das kann sogar ein Grundelement wie eine Zahl oder eine Zeichenfolge sein, aber es ist besser, Objekte zu verwenden, vorzugsweise mit name
und message
(um einigermaßen kompatibel mit eingebauten Fehlern zu bleiben).
JavaScript verfügt über viele integrierte Konstruktoren für Standardfehler: Error
, SyntaxError
, ReferenceError
, TypeError
und andere. Wir können sie auch zum Erstellen von Fehlerobjekten verwenden.
Ihre Syntax ist:
let error = new Error(message); // oder let error = new SyntaxError(message); let error = new ReferenceError(message); // ...
Bei integrierten Fehlern (nicht bei Objekten, nur bei Fehlern) ist die name
genau der Name des Konstruktors. Und message
wird aus dem Argument entnommen.
Zum Beispiel:
let error = new Error("Dinge passieren o_O"); alarm(error.name); // Fehler alarm(error.message); // Dinge passieren o_O
Mal sehen, welche Art von Fehler JSON.parse
generiert:
versuchen { JSON.parse("{ bad json o_O }"); } Catch (Err) { alarm(err.name); // SyntaxError alarm(err.message); // Unerwartetes Token b in JSON an Position 2 }
Wie wir sehen können, ist das ein SyntaxError
.
Und in unserem Fall ist das Fehlen des name
ein Fehler, da Benutzer einen name
haben müssen.
Also lass es uns werfen:
let json = '{ "age": 30 }'; // unvollständige Daten versuchen { let user = JSON.parse(json); // <-- keine Fehler if (!user.name) { throw new SyntaxError("Unvollständige Daten: kein Name"); // (*) } alarm( user.name ); } Catch (Err) { alarm( "JSON-Fehler: " + err.message ); // JSON-Fehler: Unvollständige Daten: kein Name }
In der Zeile (*)
generiert der throw
-Operator einen SyntaxError
mit der angegebenen message
, genauso wie JavaScript ihn selbst generieren würde. Die Ausführung von try
stoppt sofort und der Kontrollfluss springt in catch
.
Jetzt ist catch
ein zentraler Ort für die gesamte Fehlerbehandlung geworden: sowohl für JSON.parse
als auch für andere Fälle.
Im obigen Beispiel verwenden wir try...catch
um falsche Daten zu verarbeiten. Aber ist es möglich, dass innerhalb des try {...}
-Blocks ein weiterer unerwarteter Fehler auftritt? Wie ein Programmierfehler (Variable ist nicht definiert) oder etwas anderes, nicht nur diese Sache mit „falschen Daten“.
Zum Beispiel:
let json = '{ "age": 30 }'; // unvollständige Daten versuchen { user = JSON.parse(json); // <-- habe vergessen, „let“ vor „user“ zu setzen // ... } Catch (Err) { alarm("JSON-Fehler: " + err); // JSON-Fehler: ReferenceError: Benutzer ist nicht definiert // (eigentlich kein JSON-Fehler) }
Natürlich ist alles möglich! Programmierer machen Fehler. Selbst in Open-Source-Dienstprogrammen, die seit Jahrzehnten von Millionen Menschen genutzt werden, kann plötzlich ein Fehler entdeckt werden, der zu schrecklichen Hacks führt.
In unserem Fall wird try...catch
platziert, um „falsche Daten“-Fehler abzufangen. Aber es liegt in der Natur von catch
, dass alle Fehler von try
abgerufen werden. Hier wird ein unerwarteter Fehler angezeigt, es wird jedoch immer noch die gleiche "JSON Error"
-Meldung angezeigt. Das ist falsch und erschwert auch das Debuggen des Codes.
Um solche Probleme zu vermeiden, können wir die „Rethrowing“-Technik anwenden. Die Regel ist einfach:
Catch sollte nur Fehler verarbeiten, die ihm bekannt sind, und alle anderen „erneut auslösen“.
Die „Rethrowing“-Technik lässt sich detaillierter erklären als:
Catch erhält alle Fehler.
Im Block catch (err) {...}
analysieren wir das Fehlerobjekt err
.
Wenn wir nicht wissen, wie wir damit umgehen sollen, throw err
.
Normalerweise können wir den Fehlertyp mit dem instanceof
überprüfen:
versuchen { user = { /*...*/ }; } Catch (Err) { if (err Instanz von ReferenceError) { alarm('ReferenceError'); // „ReferenceError“ für den Zugriff auf eine undefinierte Variable } }
Wir können den Namen der Fehlerklasse auch aus der Eigenschaft err.name
abrufen. Alle nativen Fehler haben es. Eine andere Möglichkeit besteht darin, err.constructor.name
zu lesen.
Im folgenden Code verwenden wir Rethrowing, sodass catch
nur SyntaxError
behandelt:
let json = '{ "age": 30 }'; // unvollständige Daten versuchen { let user = JSON.parse(json); if (!user.name) { throw new SyntaxError("Unvollständige Daten: kein Name"); } blabla(); // unerwarteter Fehler alarm( user.name ); } Catch (Err) { if (err Instanz von SyntaxError) { alarm( "JSON-Fehler: " + err.message ); } anders { wirf irr; // erneut werfen (*) } }
Der Fehler, der in Zeile (*)
aus dem Inneren catch
Blocks ausgelöst wird, „fällt“ aus try...catch
und kann entweder von einem äußeren try...catch
Konstrukt (sofern vorhanden) abgefangen werden oder das Skript abbrechen.
Der catch
-Block behandelt also tatsächlich nur Fehler, mit denen er umzugehen weiß, und „überspringt“ alle anderen.
Das folgende Beispiel zeigt, wie solche Fehler durch eine weitere Ebene von try...catch
abgefangen werden können:
Funktion readData() { let json = '{ "age": 30 }'; versuchen { // ... blabla(); // Fehler! } Catch (Err) { // ... if (!(err Instanz von SyntaxError)) { wirf irr; // erneut werfen (weiß nicht, wie ich damit umgehen soll) } } } versuchen { readData(); } Catch (Err) { alarm( "Externer Fang erhalten: " + err ); // Habe es gefangen! }
Hier weiß readData
nur, wie mit SyntaxError
umzugehen ist, während der äußere try...catch
weiß, wie mit allem umzugehen ist.
Warten Sie, das ist noch nicht alles.
Das try...catch
Konstrukt kann eine weitere Codeklausel enthalten: finally
.
Wenn es existiert, wird es in allen Fällen ausgeführt:
Wenn es nach try
keine Fehler gab,
nach catch
, wenn Fehler aufgetreten sind.
Die erweiterte Syntax sieht so aus:
versuchen { ... versuchen Sie, den Code auszuführen ... } Catch (Err) { ... mit Fehlern umgehen ... } Endlich { ... immer ausführen ... }
Versuchen Sie, diesen Code auszuführen:
versuchen { alarm( 'try' ); if (confirm('Einen Fehler machen?')) BAD_CODE(); } Catch (Err) { alarm( 'catch' ); } Endlich { alarm( 'endlich' ); }
Der Code kann auf zwei Arten ausgeführt werden:
Wenn Sie auf „Einen Fehler machen?“ mit „Ja“ antworten, try -> catch -> finally
.
Wenn Sie „Nein“ sagen, dann try -> finally
.
Die finally
Klausel wird oft verwendet, wenn wir etwas tun und es auf jeden Fall zu einem Ergebnis bringen wollen.
Beispielsweise möchten wir die Zeit messen, die eine Fibonacci-Zahlenfunktion fib(n)
benötigt. Natürlich können wir mit der Messung beginnen, bevor es läuft, und danach fertig werden. Was aber, wenn beim Funktionsaufruf ein Fehler auftritt? Insbesondere die Implementierung von fib(n)
im folgenden Code gibt einen Fehler für negative oder nicht ganzzahlige Zahlen zurück.
Der finally
ist ein großartiger Ort, um die Messungen abzuschließen, egal was passiert.
Hier ist finally
gewährleistet, dass die Zeit in beiden Situationen korrekt gemessen wird – im Falle einer erfolgreichen Ausführung von fib
und im Falle eines Fehlers darin:
let num = +prompt("Geben Sie eine positive Ganzzahl ein?", 35) lass diff, Ergebnis; Funktion fib(n) { if (n < 0 || Math.trunc(n) != n) { throw new Error("Darf nicht negativ und auch eine ganze Zahl sein."); } n <= 1 zurückgeben? n: fib(n - 1) + fib(n - 2); } let start = Date.now(); versuchen { result = fib(num); } Catch (Err) { Ergebnis = 0; } Endlich { diff = Date.now() - start; } alarm(result || "Fehler ist aufgetreten"); Alert( `Ausführung dauerte ${diff}ms` );
Sie können dies überprüfen, indem Sie den Code ausführen, indem Sie 35
in prompt
eingeben – er wird normal ausgeführt, finally
nach try
. Geben Sie dann -1
ein – es wird sofort ein Fehler angezeigt und die Ausführung dauert 0ms
. Beide Messungen wurden korrekt durchgeführt.
Mit anderen Worten: Die Funktion kann mit return
oder throw
enden, das spielt keine Rolle. Die finally
Klausel wird in beiden Fällen ausgeführt.
Variablen sind innerhalb von try...catch...finally
lokal
Bitte beachten Sie, dass result
und diff
-Variablen im obigen Code vor try...catch
deklariert werden.
Wenn wir andernfalls let
in try
-Block deklarieren würden, wäre er nur darin sichtbar.
finally
und return
Die finally
Klausel funktioniert für jeden Exit von try...catch
. Dazu gehört auch eine explizite return
.
Im folgenden Beispiel gibt es eine return
in try
. In diesem Fall wird finally
ausgeführt, kurz bevor die Steuerung zum äußeren Code zurückkehrt.
Funktion func() { versuchen { Rückgabe 1; } Catch (Err) { /* ... */ } Endlich { alarm( 'endlich' ); } } alarm( func() ); // Zuerst funktioniert die Warnung von „Endlich“ und dann diese
try...finally
Das try...finally
-Konstrukt ohne catch
Klausel ist ebenfalls nützlich. Wir wenden es an, wenn wir Fehler hier nicht behandeln (durchfallen lassen) möchten, aber sicher sein wollen, dass von uns gestartete Prozesse abgeschlossen werden.
Funktion func() { // Beginnen Sie mit etwas, das abgeschlossen werden muss (z. B. Messungen) versuchen { // ... } Endlich { // Schließe das Ding ab, auch wenn alle sterben } }
Im obigen Code tritt immer ein Fehler innerhalb try
auf, da kein catch
vorhanden ist. Funktioniert aber finally
bevor der Ausführungsfluss die Funktion verlässt.
Umgebungsspezifisch
Die Informationen aus diesem Abschnitt sind nicht Teil des Kern-JavaScript.
Stellen wir uns vor, wir haben einen schwerwiegenden Fehler außerhalb von try...catch
und das Skript ist gestorben. Wie ein Programmierfehler oder eine andere schreckliche Sache.
Gibt es eine Möglichkeit, auf solche Vorkommnisse zu reagieren? Möglicherweise möchten wir den Fehler protokollieren, dem Benutzer etwas anzeigen (normalerweise werden ihm keine Fehlermeldungen angezeigt) usw.
In der Spezifikation gibt es keine, aber Umgebungen bieten es normalerweise an, weil es wirklich nützlich ist. Node.js verfügt beispielsweise über process.on("uncaughtException")
dafür. Und im Browser können wir der speziellen Eigenschaft window.onerror eine Funktion zuweisen, die im Falle eines nicht erkannten Fehlers ausgeführt wird.
Die Syntax:
window.onerror = function(message, url, line, col, error) { // ... };
message
Fehlermeldung.
url
URL des Skripts, bei dem der Fehler aufgetreten ist.
line
, col
Zeilen- und Spaltennummern, in denen der Fehler aufgetreten ist.
error
Fehlerobjekt.
Zum Beispiel:
<Skript> window.onerror = function(message, url, line, col, error) { Alert(`${message}n At ${line}:${col} of ${url}`); }; Funktion readData() { badFunc(); // Hoppla, etwas ist schief gelaufen! } readData(); </script>
Die Rolle des globalen Handlers window.onerror
besteht in der Regel nicht darin, die Skriptausführung wiederherzustellen – das ist bei Programmierfehlern wahrscheinlich unmöglich, sondern darin, die Fehlermeldung an Entwickler zu senden.
Es gibt auch Webdienste, die für solche Fälle eine Fehlerprotokollierung bereitstellen, wie https://errorception.com oder https://www.muscula.com.
Sie funktionieren so:
Wir registrieren uns beim Dienst und erhalten von ihnen ein Stück JS (oder eine Skript-URL), um es auf Seiten einzufügen.
Dieses JS-Skript legt eine benutzerdefinierte window.onerror
-Funktion fest.
Wenn ein Fehler auftritt, sendet er eine entsprechende Netzwerkanfrage an den Dienst.
Wir können uns bei der Service-Weboberfläche anmelden und Fehler sehen.
Das try...catch
Konstrukt ermöglicht die Behandlung von Laufzeitfehlern. Es ermöglicht buchstäblich, den Code auszuführen und Fehler zu „fangen“, die darin auftreten können.
Die Syntax lautet:
versuchen { // diesen Code ausführen } Catch (Err) { // wenn ein Fehler aufgetreten ist, dann hierher springen // err ist das Fehlerobjekt } Endlich { // auf jeden Fall nach try/catch machen }
Möglicherweise gibt es keinen Abschnitt catch
oder „ finally
, daher sind auch kürzere Konstrukte try...catch
und try...finally
gültig.
Fehlerobjekte haben folgende Eigenschaften:
message
– die für Menschen lesbare Fehlermeldung.
name
– die Zeichenfolge mit dem Fehlernamen (Name des Fehlerkonstruktors).
stack
(nicht standardmäßig, aber gut unterstützt) – der Stack zum Zeitpunkt der Fehlererstellung.
Wenn ein Fehlerobjekt nicht benötigt wird, können wir es weglassen, indem wir catch {
anstelle von catch (err) {
verwenden.
Mit dem throw
-Operator können wir auch eigene Fehler generieren. Technisch gesehen kann das Argument von throw
alles sein, aber normalerweise ist es ein Fehlerobjekt, das von der integrierten Error
-Klasse erbt. Mehr zum Erweitern von Fehlern im nächsten Kapitel.
Das erneute Auslösen ist ein sehr wichtiges Muster der Fehlerbehandlung: Ein catch
-Block erwartet normalerweise den bestimmten Fehlertyp und weiß, wie er damit umgehen soll, daher sollte er Fehler, die er nicht kennt, erneut auslösen.
Selbst wenn wir nicht über try...catch
verfügen, können wir in den meisten Umgebungen einen „globalen“ Fehlerbehandler einrichten, um Fehler abzufangen, die „ausfallen“. Im Browser ist das window.onerror
.
Wichtigkeit: 5
Vergleichen Sie die beiden Codefragmente.
Der erste verwendet finally
um den Code nach try...catch
auszuführen:
versuchen { Arbeit Arbeit } Catch (Err) { mit Fehlern umgehen } Endlich { Räumen Sie den Arbeitsbereich auf }
Das zweite Fragment platziert die Reinigung direkt nach try...catch
:
versuchen { Arbeit Arbeit } Catch (Err) { mit Fehlern umgehen } Räumen Sie den Arbeitsbereich auf
Das Aufräumen nach der Arbeit brauchen wir auf jeden Fall, egal ob ein Fehler aufgetreten ist oder nicht.
Gibt es hier einen Vorteil in der Verwendung von finally
oder sind beide Codefragmente gleich? Wenn es einen solchen Vorteil gibt, dann geben Sie ein Beispiel, wenn es darauf ankommt.
Der Unterschied wird deutlich, wenn wir uns den Code innerhalb einer Funktion ansehen.
Das Verhalten ist anders, wenn es zu einem „Herausspringen“ von try...catch
kommt.
Zum Beispiel, wenn es einen return
innerhalb von try...catch
gibt. Die finally
Klausel funktioniert im Falle eines Ausstiegs aus try...catch
, sogar über die return
-Anweisung: direkt nachdem try...catch
abgeschlossen ist, aber bevor der aufrufende Code die Kontrolle erhält.
Funktion f() { versuchen { alarm('start'); „Ergebnis“ zurückgeben; } Catch (Err) { /// ... } Endlich { alarm('cleanup!'); } } F(); // Aufräumen!
…Oder wenn es einen throw
gibt, wie hier:
Funktion f() { versuchen { alarm('start'); throw new Error("ein Fehler"); } Catch (Err) { // ... if("Kann den Fehler nicht behandeln") { wirf irr; } } Endlich { alarm('Aufräumen!') } } F(); // Aufräumen!
Es ist finally
das Garant für die Bereinigung hier. Wenn wir den Code einfach am Ende von f
einfügen würden, würde er in diesen Situationen nicht ausgeführt.