Try-Catch-Finally in JavaScript: Wie funktioniert das eigentlich?

Max Schneider
Max Schneider ·
Serverless Horror

Try-Catch-Finally in JavaScript: Wie funktioniert das eigentlich?

In letzter Zeit habe ich mich intensiv mit dem Thema Fehlerbehandlung in JavaScript beschäftigt. Dabei bin ich auf eine spannende Frage gestoßen: Wie funktioniert der finally-Block genau? Wird er wirklich immer ausgeführt, selbst wenn ein Fehler geworfen wird? Und warum ist das so?

Um diese Fragen zu beantworten, habe ich den folgenden Code geschrieben und analysiert:

function connectDB() {
  console.log("DB Connected");
}
 
function getUserByName(name) {
  // Prüfen, ob der Name ein String ist
  if (typeof name !== "string") {
    throw Error("Name must be a String");
  }
 
  const users = [
    { name: "Max", age: 26 },
    { name: "Tim", age: 25 },
  ];
 
  const user = users.filter(function (user) {
    return user.name === name;
  });
 
  console.log(user);
  return user;
}
 
function closeDB() {
  console.log("Close DB");
}
 
function fetchUsers() {
  connectDB();
  try {
    // Simuliert einen Fehler durch falschen Input
    const user = getUserByName(2);
    console.log(user[0]);
  } catch (error) {
    // Einen neuen Fehler werfen
    throw new Error("FetchUsers Error:" + error);
  } finally {
    closeDB(); // Immer ausführen
  }
}
 
function main() {
  try {
    fetchUsers();
  } catch (error) {
    console.log(error);
  }
}
 
main();

Was passiert in diesem Code?

Wenn der Code ausgeführt wird, erscheint folgendes in der Konsole:

DB Connected
Close DB
Error: FetchUsers Error:Error: Name must be a String
    at fetchUsers (<Pfad>/index.js:34:11)
    at main (<Pfad>/index.js:42:5)
    at Object.<anonymous> (<Pfad>/index.js:48:1)

Hier ist der Ablauf Schritt für Schritt erklärt:

  1. connectDB() wird aufgerufen: Die Datenbank wird "verbunden", und "DB Connected" wird in die Konsole geschrieben.
  2. Fehler in getUserByName: Da der Eingabewert 2 kein String ist, wirft die Funktion getUserByName den Fehler "Name must be a String".
  3. catch in fetchUsers: Der Fehler wird im try-catch-Block der Funktion fetchUsers abgefangen. Ein neuer Fehler wird erzeugt und mit der Nachricht "FetchUsers Error:" verpackt.
  4. finally wird ausgeführt: Bevor der neue Fehler propagiert wird, stellt der finally-Block sicher, dass die Datenbankverbindung geschlossen wird. Dies schreibt "Close DB" in die Konsole.
  5. Fehlerpropagierung zu main: Der neue Fehler wird an die Funktion main weitergeleitet, wo er abgefangen und ausgegeben wird.

Die zentrale Frage: Warum wird der finally-Block ausgeführt, bevor der Fehler propagiert wird?

Unter der Haube speichert die JavaScript-Engine den Fehler, der durch throw ausgelöst wurde, zwischen. Die Ausführung des finally-Blocks hat Vorrang, weil er für wichtige Aufräumarbeiten gedacht ist. Erst wenn der finally-Block abgeschlossen ist, wird der gespeicherte Fehler weitergereicht.

So funktioniert es:

  1. throw wirft einen Fehler: Sobald ein Fehler auftritt, pausiert die Engine die Fehlerpropagierung.
  2. Ausführen des finally-Blocks: Falls ein finally-Block existiert, wird er komplett ausgeführt.
  3. Fortsetzen der Propagierung: Nachdem der finally-Block beendet ist, setzt die Engine die Fehlerweiterleitung fort.

Warum ist das sinnvoll?

Der finally-Block wurde speziell für Situationen wie diese entworfen, um sicherzustellen, dass wichtige Aufräumarbeiten oder abschließende Schritte ausgeführt werden – egal, ob ein Fehler auftritt oder nicht. Würde die Fehlerpropagierung den finally-Block überspringen, könnte das zu ernsthaften Problemen wie Ressourcenlecks führen.

Ein Beispiel: In meinem Code ruft der finally-Block closeDB() auf, um die Datenbankverbindung zu schließen. Ohne diesen Mechanismus wäre die Verbindung möglicherweise offen geblieben, was ineffizient und riskant ist.

Fazit

Die Fehlerbehandlung in JavaScript mag auf den ersten Blick verwirrend erscheinen, aber sie ist unglaublich leistungsfähig und durchdacht. Der finally-Block ist ein unverzichtbares Werkzeug, um zu garantieren, dass kritische Aufgaben wie das Schließen von Verbindungen oder das Freigeben von Ressourcen ausgeführt werden, bevor das Programm weitergeht oder beendet wird.

Durch diese Analyse habe ich gelernt, dass der finally-Block immer ausgeführt wird, bevor der Fehler weiter propagiert wird. Das macht ihn zu einem Schlüsselelement für robusten und sicheren Code.

Ich hoffe, dieser Beitrag hilft dir, das Verhalten von try-catch-finally besser zu verstehen! Hast du Fragen oder eigene Erfahrungen dazu? Lass es mich wissen! :)

Bonus (Quiz):

// Quiz: Welche Reihenfolge wird in der Konsole ausgegeben?
 
function test() {
  try {
    console.log("Start");
    throw new Error("Error");
  } catch (error) {
    throw error;
  } finally {
    console.log("Finally block executed");
  }
}
 
try {
  test();
} catch (error) {
  console.log(error.message);
}
 
// Frage: Welche Reihenfolge wird in der Konsole ausgegeben?
// a) "Start", "Error", "Finally block executed"
// b) "Finally block executed", "Start", "Error"
// c) "Start", "Finally block executed", "Error"
Max

Hallo 👋 Ich bin Max dein freiberuflicher Softwareentwickler und Author des Blogs. Du kannst meine Arbeit auf Social Media verfolgen.

Mehr Blogbeiträge

Funktionsarten in JavaScript verstehen: Mehr als nur Syntax
JavaScript
Max Schneider
Max Schneider
20.3.2025

Funktionsarten in JavaScript verstehen: Mehr als nur Syntax

In JavaScript gibt es verschiedene Arten, Funktionen zu definieren, und die Wahl der richtigen Funktionsart ist nicht nur eine Frage des persönlichen Stils, sondern kann entscheidend für die Funktionalität und Wartbarkeit deines Codes sein.