HOWTO · JavaScript

Try ohne Catch in JavaScript

Im heutigen Beitrag lernen wir try-Anweisungen kennen, ohne catch in JavaScript zu implementieren.

Auf dieser Seite

Ja — eine JavaScript-try-Anweisung kann catch weglassen, solange eine finally-Klausel vorhanden ist. Dieses Tutorial erklärt, wann try ohne catch gültig ist, was mit einem geworfenen Fehler passiert und wann diese Form das richtige Werkzeug ist.

So funktioniert try ohne catch in JavaScript

Die JavaScript-try-Anweisung umschließt Code, der einen Fehler auslösen kann, und ist überall dort gültig, wo eine Anweisung erlaubt ist: auf der obersten Ebene eines Skripts, innerhalb einer Funktion oder verschachtelt in einem anderen try. Was sie nicht kann, ist allein zu stehen — auf einen try-Block muss eine catch-Klausel, eine finally-Klausel oder beides folgen; ein nacktes try ohne beides ist ein Syntaxfehler. Die geschweiften Klammern {} sind immer erforderlich, auch für eine einzelne Anweisung.

Syntax:

Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.

try {
  // try_statements: code that may throw
} catch (exception_var) {
  // catch_statements: handle the thrown value
} finally {
  // finally_statements: always run afterward
}
  1. try_statements sind die auszuführenden Anweisungen.
  2. catch_statements sind die Anweisungen, die ausgeführt werden, wenn im try-Block ein Wert geworfen wird.
  3. exception_var ist eine optionale Kennung, die den geworfenen Wert für den zugehörigen catch-Block enthält.
  4. finally_statements werden nach Abschluss der try-Anweisung ausgeführt, unabhängig davon, ob ein Wert geworfen oder abgefangen wurde.

Da catch nur dann optional ist, wenn finally vorhanden ist (und umgekehrt), erlaubt die Grammatik genau drei Kombinationen:

  1. try...catch
  2. try...finally
  3. try...catch...finally

Wenn ein catch-Block vorhanden ist und eine Anweisung im try-Block (oder eine von ihr aufgerufene Funktion) wirft, wechselt die Steuerung sofort zu catch; wenn nichts geworfen wird, wird catch vollständig übersprungen. Der finally-Block läuft immer, nachdem die try- und catch-Blöcke abgeschlossen sind — unabhängig davon, ob ein Wert geworfen wurde und ob catch ihn behandelt hat.

Ohne catch-Klausel läuft finally trotzdem zuerst, und danach propagiert der geworfene Wert weiter zum Aufrufer. Das gilt auch für Verschachtelungen: try-Anweisungen können verschachtelt werden, und wenn eine innere try-Anweisung kein catch hat, behandelt stattdessen der catch-Block einer umschließenden try-Anweisung den Fehler.

Die vollständige Grammatik finden Sie in der MDN-Dokumentation zu try...catch.

Beispiel: try...finally ohne catch

Das folgende Snippet platziert ein try...finally ohne eigenes catch innerhalb eines äußeren try...catch, sodass wir beobachten können, wie der finally-Block läuft und wo der geworfene Wert landet. 0 / 0 ergibt NaN, sodass throw new Error(0 / 0) ein Error-Objekt wirft, dessen Meldung NaN ist. Die Texte im Code und in der Ausgabe bleiben unverändert auf Englisch.

Führen Sie dieses Beispiel aus:

Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.

try {
  try {
    console.log('Executing try block')
    throw new Error(0 / 0)
  } finally {
    console.log('Final Call')
  }
} catch (error) {
  console.log('Outer catch received: ' + error.message)
}
Executing try block
Final Call
Outer catch received: NaN

Das innere try gibt seine Log-Zeile Executing try block aus und wirft dann. Da es kein catch hat, wechselt die Steuerung direkt zu finally, das Final Call ausgibt. Erst nachdem finally abgeschlossen ist, propagiert der Error weiter; dort empfängt ihn der äußere catch und gibt seine NaN-Meldung aus. Ohne dieses umschließende catch würde derselbe Fehler den Aufrufer erreichen oder — auf oberster Ebene — nach der Ausgabe von Final Call als unbehandelter Fehler auftreten.

Wann man try ohne catch verwendet

Greifen Sie zu try...finally, wenn ein Block garantierte Aufräumarbeiten braucht, der Fehler selbst aber dem Aufrufer gehört. Typische finally-Aufgaben sind das Freigeben einer Ressource, das Schließen einer Verbindung oder eines Datei-Handles, das Wiederherstellen gemeinsamen Zustands — etwa eines erneut aktivierten Buttons oder eines zurückgesetzten Flags — und das Stoppen eines Timers: Arbeiten, die erfolgen müssen, ob der try-Block nun normal endet oder wirft.

Diese Form ist die richtige Wahl, wenn der Code dem Fehler nichts Sinnvolles hinzuzufügen hat: Ein unverändertes erneutes Werfen erzeugt nur Rauschen, während ein leerer catch-Block jeden Fehler stillschweigend verschluckt, sodass Bugs weit entfernt von ihrer Ursache auftauchen. try...finally bewahrt die Aufräumgarantie und lässt den Aufrufer den Fehler trotzdem sehen und behandeln. Wenn die Funktion den geworfenen Wert wirklich untersuchen, protokollieren oder daraus wiederherstellen muss, fügen Sie stattdessen eine echte catch-Klausel hinzu.

Einschränkungen und Fallstricke von try ohne catch

  • finally behandelt oder unterdrückt den Fehler niemals. Nachdem finally abgeschlossen ist, propagiert ein unbehandelter geworfener Wert weiter zum Aufrufer; auf oberster Ebene beendet er das Skript oder meldet einen unbehandelten Fehler.
  • Sie können den geworfenen Wert nicht beobachten. Ohne catch hat das Konstrukt keinen Zugriff auf das Geworfene und kann daher weder die Meldung protokollieren noch je nach Fehlertyp verzweigen.
  • Die Kontrollflusssteuerung innerhalb von finally überschreibt alles, was noch ausstand. Ein return oder throw in finally verwirft einen noch propagierenden Wert — einschließlich eines geworfenen Fehlers, der dann still verschwindet —; beschränken Sie finally daher auf einfache Aufräumarbeiten, die weder zurückgeben noch werfen.
  • Anweisungen nach einem try...finally laufen nur, wenn nichts geworfen wurde, und finally selbst darf nicht davon abhängen, dass der try-Block erfolgreich war, da es auch auf dem Fehlerpfad läuft.