Signatur
Beschreibung
Die try...catch-Anweisung besteht aus einem try-Block und entweder einem catch-Block, einem finally-Block oder beiden. Der Code im try-Block wird zuerst ausgeführt, und falls dabei eine Ausnahme geworfen wird, wird der Code im catch-Block ausgeführt. Der Code im finally-Block wird immer ausgeführt, bevor der Kontrollfluss das gesamte Konstrukt verlässt.
Die try-Anweisung beginnt immer mit einem try-Block. Anschließend muss ein catch-Block oder ein finally-Block vorhanden sein. Es ist auch möglich, sowohl einen catch- als auch einen finally-Block zu haben. Damit ergeben sich drei Formen der try-Anweisung:
try...catchtry...finallytry...catch...finally
Anders als andere Konstrukte wie if oder for müssen die try-, catch- und finally-Blöcke Blöcke sein, statt einzelner Anweisungen.
Ein catch-Block enthält Anweisungen, die festlegen, was zu tun ist, wenn im try-Block eine Ausnahme geworfen wird. Wenn irgendeine Anweisung innerhalb des try-Blocks (oder in einer aus dem try-Block heraus aufgerufenen Funktion) eine Ausnahme wirft, wird die Kontrolle sofort an den catch-Block übergeben. Wird im try-Block keine Ausnahme geworfen, wird der catch-Block übersprungen.
Der finally-Block wird immer ausgeführt, bevor der Kontrollfluss das try...catch...finally-Konstrukt verlässt. Er wird immer ausgeführt, unabhängig davon, ob eine Ausnahme geworfen oder gefangen wurde.
Man kann eine oder mehrere try-Anweisungen verschachteln. Wenn eine innere try-Anweisung keinen catch-Block hat, wird stattdessen der catch-Block der umschließenden try-Anweisung verwendet.
Man kann die try-Anweisung auch verwenden, um JavaScript-Ausnahmen zu behandeln.
Catch-Binding
Wenn im try-Block eine Ausnahme geworfen wird, enthält exceptionVar (also das e in catch (e)) den Ausnahmewert. Dieses Binding kann verwendet werden, um Informationen über die geworfene Ausnahme zu erhalten. Dieses Binding ist nur im Scope des catch-Blocks verfügbar.
Es muss kein einzelner Identifier sein. Man kann ein Destructuring-Pattern verwenden, um mehrere Identifier gleichzeitig zuzuweisen.
Die durch die catch-Klausel erzeugten Bindings leben im selben Scope wie der catch-Block, daher können Variablen, die im catch-Block deklariert werden, nicht denselben Namen wie die durch die catch-Klausel erzeugten Bindings haben. (Es gibt eine Ausnahme von dieser Regel, aber sie ist eine veraltete Syntax.)
Das Exception-Binding ist beschreibbar. Beispielsweise möchte man den Ausnahmewert eventuell normalisieren, um sicherzustellen, dass es sich um ein Error-Objekt handelt.
Wenn der Ausnahmewert nicht benötigt wird, kann er zusammen mit den umschließenden Klammern weggelassen werden.
Der finally-Block
Der finally-Block enthält Anweisungen, die nach der Ausführung des try-Blocks und des/der catch-Block/-Blöcke ausgeführt werden, aber vor den Anweisungen, die auf den try...catch...finally-Block folgen. Der Kontrollfluss betritt immer den finally-Block, was auf eine der folgenden Weisen erfolgen kann:
- Unmittelbar nachdem der Kontrollfluss den
try-Block in einemtry...finally-Konstrukt verlässt (entweder nach der letzten Anweisung oder einerthrow-,return-,break- odercontinue-Anweisung); - Unmittelbar nachdem der Kontrollfluss den
catch-Block in einemtry...catch...finally-Konstrukt verlässt; - Unmittelbar nachdem der Kontrollfluss den
try-Block in einemtry...catch...finally-Konstrukt verlässt, außer er verlässt ihn über einethrow-Anweisung (in diesem Fall betritt der Kontrollfluss zuerst dencatch-Block).
Wird der finally-Block nach einer Kontrollfluss-Anweisung (return, throw, break, continue) im try- oder catch-Block betreten, wird die Wirkung dieser Anweisung aufgeschoben, bis die letzte Anweisung im finally-Block ausgeführt wurde. Wenn beispielsweise eine Ausnahme aus dem try-Block geworfen wird, wird der finally-Block auch dann noch ausgeführt, wenn es keinen catch-Block zur Behandlung der Ausnahme gibt, und die Ausnahme wird unmittelbar nach Abschluss des finally-Blocks geworfen.
Es gibt jedoch eine Ausnahme von dieser Regel: Wenn die letzte im finally-Block ausgeführte Anweisung selbst eine Kontrollfluss-Anweisung ist, überschreibt diese Anweisung die Wirkung der vorherigen (keine Verzögerung); siehe „Rückgabe aus einem finally-Block" für Beispiele. Es ist generell eine schlechte Idee, Kontrollfluss-Anweisungen (return, throw, break, continue) im finally-Block zu verwenden, da sie die Wirkung zuvor ausgeführter Kontrollfluss-Anweisungen überschreiben können, was selten beabsichtigt ist. In den meisten Fällen sollte der finally-Block für Aufräumcode reserviert sein, der die Hauptlogik nicht verändert.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $tryStatements Pflicht | Statements | — | Die auszuführenden Anweisungen. |
| $catchStatements | Statements | — | Anweisung, die ausgeführt wird, wenn im try-Block eine Ausnahme geworfen wird. |
| $exceptionVar | Identifier | Pattern | — | Ein optionaler Identifier oder Pattern, der die abgefangene Ausnahme für den zugehörigen catch-Block hält. Wenn der catch-Block den Ausnahmewert nicht verwendet, kann exceptionVar zusammen mit den umschließenden Klammern weggelassen werden. |
| $finallyStatements | Statements | — | Anweisungen, die ausgeführt werden, bevor der Kontrollfluss das try...catch...finally-Konstrukt verlässt. Diese Anweisungen werden unabhängig davon ausgeführt, ob eine Ausnahme geworfen oder gefangen wurde. |
Rückgabewert
try...catch ist eine Anweisung (Statement) und hat keinen Rückgabewert.Beispiele
Bedingungsloser catch-Block
try {
throw new Error("My exception"); // generates an exception
} catch (e) {
// statements to handle any exceptions
logMyErrors(e); // pass exception object to error handler
}
Destructuring der Exception
try {
throw new TypeError("oops");
} catch ({ name, message }) {
console.log(name); // "TypeError"
console.log(message); // "oops"
}
Exception-Binding überschreiben
try {
throw "Oops; this is not an Error object";
} catch (e) {
if (!(e instanceof Error)) {
e = new Error(e);
}
console.error(e.message);
}
Optionales Catch-Binding
function isValidJSON(text) {
try {
JSON.parse(text);
return true;
} catch {
return false;
}
}
Bedingte catch-Blöcke
try {
myRoutine(); // may throw three types of exceptions
} catch (e) {
if (e instanceof TypeError) {
// statements to handle TypeError exceptions
} else if (e instanceof RangeError) {
// statements to handle RangeError exceptions
} else if (e instanceof EvalError) {
// statements to handle EvalError exceptions
} else {
// statements to handle any unspecified exceptions
logMyErrors(e); // pass exception object to error handler
}
}
Erwartete Fehler abfangen, andere weiterwerfen
try {
myRoutine();
} catch (e) {
if (e instanceof RangeError) {
// statements to handle this very common expected error
} else {
throw e; // re-throw the error unchanged
}
}
Verschachtelte try-Blöcke
try {
try {
throw new Error("oops");
} finally {
console.log("finally");
}
} catch (ex) {
console.error("outer", ex.message);
}
// Logs:
// "finally"
// "outer" "oops"
Verschachtelt mit innerem catch
try {
try {
throw new Error("oops");
} catch (ex) {
console.error("inner", ex.message);
} finally {
console.log("finally");
}
} catch (ex) {
console.error("outer", ex.message);
}
// Logs:
// "inner" "oops"
// "finally"
Fehler erneut werfen
try {
try {
throw new Error("oops");
} catch (ex) {
console.error("inner", ex.message);
throw ex;
} finally {
console.log("finally");
}
} catch (ex) {
console.error("outer", ex.message);
}
// Logs:
// "inner" "oops"
// "finally"
// "outer" "oops"
Ressourcenaufräumen mit finally
openMyFile();
try {
// tie up a resource
writeMyFile(theData);
} finally {
closeMyFile(); // always close the resource
// any uncaught exception is deferred here
}
return im try mit finally
function safeWriteMyFile() {
openMyFile();
try {
return writeMyFile(theData); // function call is evaluated
} finally {
closeMyFile(); // always close the resource
// return is deferred here
}
}
Rückgabe aus einem finally-Block
function doIt() {
const order = ["z"];
try {
order.push("try");
return order.sort(); // "z" is now after "try"
} finally {
order.push("finally");
return order;
}
}
doIt();
// returns ["try", "z", "finally"], not ["finally", "try", "z"] or ["try", "z"]
finally überschreibt throw
function doIt() {
try {
throw "try"; // makes control flow enter the `catch` block
} catch {
throw "catch"; // makes control flow enter the `finally` block
} finally {
return "finally"; // returns "finally" instead of throwing "catch"
}
}
doIt(); // returns "finally"
// Wichtig · Fallstricke
Nur synchrone Fehler: try...catch fängt ausschließlich Fehler ab, die synchron innerhalb des try-Blocks auftreten. Fehler in asynchronen Callbacks (setTimeout, Event-Handler) werden nicht abgefangen. Für async/await-Code funktioniert try...catch jedoch korrekt, da await ein abgelehntes Promise als synchronen Wurf behandelt.
finally überschreibt return: Ein return-Statement im finally-Block überschreibt jeden Rückgabewert aus try oder catch. Dieses Verhalten ist oft überraschend und sollte vermieden werden.
- Performance:
try...catch-Blöcke haben in modernen JS-Engines kaum messbaren Overhead, solange kein Fehler geworfen wird. Fehler zu werfen und abzufangen ist jedoch teurer als normale Kontrollfluss-Prüfungen. - Kein catch für Syntaxfehler: Syntaxfehler beim Parsen des Skripts werden nicht abgefangen (das Skript läuft gar nicht erst). Ausnahme:
eval()-Syntaxfehler können abgefangen werden. - ES2019 Optional Catch Binding: Wird von allen modernen Browsern unterstützt (Chrome 66+, Firefox 58+, Safari 11.1+, Node.js 10+).