Zum Inhalt springen
aviral gupta

// I4.1 · ca. 38 Min. · Aufbau

throw, try, catch und finally

Nach dieser Lektion lassen Sie Ihre Funktionen mit einem Error laut scheitern, behandeln die Fehler, die Sie erwarten, und lassen die anderen durch.

Lektion 1 von 5 in I4 Fehler, Debugging und Tests

Anfang des Moduls

Danach können Sie

  • Error-Objekte mit klarer Meldung werfen und sagen, welcher Code nach throw nicht mehr läuft
  • Fehler mit try...catch fangen, ihren Typ prüfen und die weiterwerfen, die Sie nicht behandeln können
  • Vorhersagen, wann finally läuft, auch nach return, und return aus finally heraushalten
  1. Aufwärmen · Aufgabe 1 von 7

    Zum Aufwärmen, aus Lektion I3.5: eine leere JSON-Datei. Was gibt dieser Code aus?

    try {
      JSON.parse("");
    } catch (error) {
      console.log(error.name);
    }
  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie es vorher, bevor Sie weiterlesen: Was gibt dieser Code aus?

    try {
      console.log("A");
      throw new Error("B");
      console.log("C");
    } catch (error) {
      console.log(error.message);
    } finally {
      console.log("D");
    }
    console.log("E");
  3. Üben · Aufgabe 3 von 7

    Ergänzen Sie den Operator, der den Typ des gefangenen Fehlers prüft.

    if (error ____ RangeError) console.log("range");
    if (error RangeError)
  4. Üben · Aufgabe 4 von 7

    try gibt einen Wert zurück. Was gibt dieser Code aus?

    function load() {
      try {
        return "data";
      } finally {
        console.log("cleanup");
      }
    }
    console.log(load());
  5. Üben · Aufgabe 5 von 7

    parse behandelt kaputtes JSON und wirft alles andere weiter. Was gibt dieser Code aus?

    function parse(text) {
      try {
        return JSON.parse(text).items.length;
      } catch (error) {
        if (error instanceof SyntaxError) return 0;
        throw error;
      }
    }
    
    try {
      console.log(parse("{}"));
    } catch (error) {
      console.log("outer:", error.name);
    }
  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. Sowohl catch als auch finally haben ein return. Was gibt dieser Code aus?

    function check() {
      try {
        throw new Error("broken");
      } catch (error) {
        return "caught " + error.message;
      } finally {
        return "finally wins";
      }
    }
    console.log(check());
  7. Anwenden · Aufgabe 7 von 7

    Kleine Aufgabe: Schreiben Sie divide(a, b). Es wirft einen TypeError, wenn a oder b keine Zahl ist, und einen RangeError, wenn b gleich 0 ist; sonst gibt es a / b zurück. Rufen Sie es in einer Schleife für [10, 4], [1, 0] und ["6", 2] auf: Geben Sie das Ergebnis aus oder Name und Meldung des Fehlers, und geben Sie in finally done mit beiden Werten aus.

    Prüfen Sie Ihr Ergebnis anhand dieser Liste

Selbst programmieren

Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.

Ausgearbeitetes Beispiel

Eingaben prüfen, erwartete Fehler behandeln

parseAge wirft einen TypeError für Text, der keine ganze Zahl ist, und einen RangeError für ein Alter außerhalb des Bereichs. Die Schleife fängt jeden Fehler, eine schlechte Eingabe stoppt die anderen also nicht, und finally meldet jede Eingabe. readSettings behandelt kaputtes JSON mit Standardwerten und wirft jeden anderen Fehler weiter.

main.js

function parseAge(text) {
  const age = Number(text);
  if (!Number.isInteger(age)) throw new TypeError(`Not a whole number: "${text}"`);
  if (age < 0 || age > 150) throw new RangeError(`Out of range: ${age}`);
  return age; // only reached when both checks pass
}

for (const input of ["42", "abc", "-3"]) {
  try {
    console.log("age", parseAge(input));
  } catch (error) {
    console.log(error.name + ": " + error.message);
  } finally {
    console.log("checked", input);
  }
}

function readSettings(text) {
  try {
    return JSON.parse(text);
  } catch (error) {
    if (error instanceof SyntaxError) return {}; // broken JSON: use defaults
    throw error; // anything else is not ours to handle
  }
}
console.log(readSettings('{"theme": "dark"}'), readSettings("{oops"));

Ausführen mit

node main.js

Ausgabe

age 42
checked 42
TypeError: Not a whole number: "abc"
checked abc
RangeError: Out of range: -3
checked -3
{ theme: 'dark' } {}
  • Das try...catch steht in der Schleife: Ein try um die ganze Schleife würde beim ersten Fehler stoppen.
  • error.name ist der Typ des Fehlers; error.message ist der Text, der dem constructor übergeben wurde.
  • readSettings entscheidet nach dem Typ: Nur ein SyntaxError bedeutet kaputtes JSON.
Ändern und ausführen

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.

Übungen

Übung 1 von 3

Ein Preis oder ein Fehler

parsePrice(text) macht aus Text wie "12.50" eine Zahl. Für Text, der keine Zahl ist, oder für leeren Text muss es einen TypeError mit der Meldung Not a price: "…" (der Text in Anführungszeichen) werfen, für eine negative Zahl einen RangeError. Der Starter gibt zurück, was Number liefert, schlechte Eingaben rutschen also als NaN oder 0 durch.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Number("abc") ist NaN und Number("") ist 0: Prüfen Sie beides, bevor Sie zurückgeben.

  2. Hinweis 2

    Number.isNaN(price) und text.trim() === "" finden die beiden schlechten Fälle.

  3. Hinweis 3

    throw new TypeError(`Not a price: "${text}"`)

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

export function parsePrice(text) {
  const price = Number(text);
  if (text.trim() === "" || Number.isNaN(price)) throw new TypeError(`Not a price: "${text}"`);
  if (price < 0) throw new RangeError(`Negative price: ${price}`);
  return price;
}
Auf dem eigenen Computer ausführen

Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.js

export function parsePrice(text) {
  return Number(text);
}

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {parsePrice} from './main.js';

test('12.50 ist 12.5', () => {
  assert.equal(parsePrice('12.50'), 12.5, 'parsePrice("12.50") sollte 12.5 sein');
});

test('abc wirft einen TypeError mit dem Text in der Meldung', () => {
  assert.throws(() => parsePrice('abc'), {name: 'TypeError', message: 'Not a price: "abc"'}, 'parsePrice("abc") sollte TypeError: Not a price: "abc" werfen');
});

test('ein leerer Text wirft einen TypeError, nicht 0', () => {
  assert.throws(() => parsePrice(''), {name: 'TypeError'}, 'parsePrice("") sollte einen TypeError werfen, aber Number("") ist 0');
});

test('ein negativer Preis wirft einen RangeError', () => {
  assert.throws(() => parsePrice('-1'), {name: 'RangeError'}, 'parsePrice("-1") sollte einen RangeError werfen');
});

package.json

{
  "type": "module"
}

package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.

Programm ausführen:

node main.js

Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):

node --test
learnrun.js herunterladen

Übung 2 von 3

Nicht jeden Fehler verschlucken

loadCart(text) liest einen als JSON gespeicherten Warenkorb: ein Array von Artikeln. Kaputtes JSON soll einen leeren Warenkorb ergeben, aber gültiges JSON, das kein Array ist, ist ein Bug an anderer Stelle und muss den TypeError Cart must be an array werfen. Das catch im Starter verschluckt jeden Fehler, dieser TypeError erreicht den Aufrufer also nie. Behandeln Sie nur SyntaxError.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Geben Sie dem catch-Block einen Namen für den Fehler, damit Sie ihn ansehen können.

  2. Hinweis 2

    error instanceof SyntaxError ist nur für kaputtes JSON true.

  3. Hinweis 3

    Für jeden anderen Fehler: throw error;

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

export function loadCart(text) {
  try {
    const cart = JSON.parse(text);
    if (!Array.isArray(cart)) throw new TypeError("Cart must be an array");
    return cart;
  } catch (error) {
    if (error instanceof SyntaxError) return [];
    throw error;
  }
}
Auf dem eigenen Computer ausführen

Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.js

export function loadCart(text) {
  try {
    const cart = JSON.parse(text);
    if (!Array.isArray(cart)) throw new TypeError("Cart must be an array");
    return cart;
  } catch {
    return [];
  }
}

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {loadCart} from './main.js';

test('ein gespeichertes Array kommt zurück', () => {
  const cart = loadCart('[{"id": 1}]');
  assert.deepEqual(cart, [{id: 1}], `loadCart ergab ${JSON.stringify(cart)}`);
});

test('kaputtes JSON ergibt einen leeren Warenkorb', () => {
  const cart = loadCart('[{"id": 1');
  assert.deepEqual(cart, [], `loadCart ergab ${JSON.stringify(cart)}`);
});

test('ein Objekt statt eines Arrays wirft den TypeError', () => {
  assert.throws(() => loadCart('{"id": 1}'), {name: 'TypeError', message: 'Cart must be an array'}, 'loadCart sollte den TypeError nicht verschlucken');
});

package.json

{
  "type": "module"
}

package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.

Programm ausführen:

node main.js

Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):

node --test
learnrun.js herunterladen

Übung 3 von 3

Immer entsperren

runLocked(lock, task) setzt lock.busy auf true, führt task() aus und gibt dessen Ergebnis zurück. Danach muss lock.busy wieder false sein, auch wenn task wirft; der Fehler selbst muss den Aufrufer trotzdem erreichen. Der Starter entsperrt nur nach einer erfolgreichen task, eine fehlgeschlagene task lässt die Sperre also für immer belegt.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Welcher Block läuft sowohl nach Erfolg als auch nach einem Fehler?

  2. Hinweis 2

    Ein catch brauchen Sie hier nicht: try...finally lässt den Fehler durch.

  3. Hinweis 3

    try { return task(); } finally { lock.busy = false; }

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

export function runLocked(lock, task) {
  lock.busy = true;
  try {
    return task();
  } finally {
    lock.busy = false;
  }
}
Auf dem eigenen Computer ausführen

Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.js

export function runLocked(lock, task) {
  lock.busy = true;
  const result = task();
  lock.busy = false;
  return result;
}

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {runLocked} from './main.js';

test('gibt das Ergebnis zurück und entsperrt', () => {
  const lock = {busy: false};
  assert.equal(runLocked(lock, () => 42), 42, 'runLocked sollte zurückgeben, was die task zurückgibt');
  assert.equal(lock.busy, false, 'lock.busy sollte nach der task false sein');
});

test('die task sieht die Sperre als belegt', () => {
  const lock = {busy: false};
  assert.equal(runLocked(lock, () => lock.busy), true, 'lock.busy sollte true sein, während die task läuft');
});

test('eine fehlschlagende task entsperrt trotzdem, und ihr Fehler erreicht den Aufrufer', () => {
  const lock = {busy: false};
  assert.throws(() => runLocked(lock, () => { throw new Error('disk full'); }), {message: 'disk full'}, 'der Fehler der task sollte den Aufrufer erreichen');
  assert.equal(lock.busy, false, 'lock.busy sollte nach einer fehlgeschlagenen task false sein');
});

package.json

{
  "type": "module"
}

package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.

Programm ausführen:

node main.js

Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):

node --test
learnrun.js herunterladen

Häufige Fehler

Einen String werfen

function findUser(id) {
  throw "User " + id + " not found";
}

try {
  findUser(7);
} catch (error) {
  console.log(error.message.toUpperCase());
}

Was Node.js ausgibt

TypeError: Cannot read properties of undefined (reading 'toUpperCase')

Warum, und die Lösung

Ein geworfener String hat weder message noch name noch stack, Code, der einen Error erwartet, bricht also, und Sie verlieren die Stelle, an der es passiert ist. Werfen Sie ein Error-Objekt: throw new Error(`User ${id} not found`). Verwenden Sie einen eingebauten Typ wie TypeError oder RangeError, wenn er passt.

Eine Variable aus einem try-Block verwenden

try {
  const settings = JSON.parse('{"theme": "dark"}');
} catch (error) {
  console.log("broken settings");
}
console.log(settings.theme);

Was Node.js ausgibt

ReferenceError: settings is not defined

Warum, und die Lösung

const und let im try-Block gibt es nur in diesem Block. Verwenden Sie den Wert innerhalb von try oder deklarieren Sie ihn davor: let settings = {}; dann try { settings = JSON.parse(text); } catch { … }, damit er auch nach einem Fehler einen Wert hat.

Ein catch, das nur loggt und weitermacht

let settings;
try {
  settings = JSON.parse("{oops");
} catch (error) {
  console.log("could not read the settings");
}
console.log(settings.theme);

Was Node.js ausgibt

TypeError: Cannot read properties of undefined (reading 'theme')

Warum, und die Lösung

Der catch-Block gab eine Zeile aus, ließ settings aber undefined, sodass das Programm ein paar Zeilen später mit einem weniger hilfreichen Fehler scheitert. Ein catch muss das Programm in einem brauchbaren Zustand hinterlassen: Geben Sie settings dort einen Standardwert oder werfen Sie den Fehler weiter, wenn das Programm nicht fortfahren kann.

JavaScript im Browser: die eigene Engine Ihres Browsers in einem abgeschotteten Worker. Syntaxfehler findet acorn 8.18.0, MIT. Lizenz und Quellcode

Abschlussquiz

5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.

Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.

Problem melden

Etwas ist falsch oder unklar? Beschreiben Sie es kurz, dann wird es geprüft und korrigiert.

#

Mindestens 20 Zeichen.

Nur, wenn Sie eine Antwort wünschen.

Kernideen

throw stoppt die Funktion

throw new Error("Out of stock") stoppt die aktuelle Funktion sofort: Keine Anweisung danach läuft, und es wird kein Wert zurückgegeben. Der Fehler wandert hinauf zum nächsten try...catch um den Aufruf, in dieser Funktion oder in einem Aufrufer. Gibt es keines, stoppt das Programm mit dem Fehler. Werfen Sie Error-Objekte, keine Strings: Ein Error hat name, message und stack, und eingebaute Typen wie TypeError und RangeError sagen, um welche Art von Problem es sich handelt.

Fangen Sie, was Sie behandeln können

try { … } catch (error) { … } führt den catch-Block aus, wenn irgendetwas im try-Block wirft, auch in Funktionen, die es aufruft. error.name und error.message beschreiben den Fehler, und error instanceof SyntaxError prüft seinen Typ. Behandeln Sie nur die Fehler, die Sie erwarten, etwa kaputtes JSON von einem Nutzer, und werfen Sie alles andere mit throw error; weiter; ein catch, das jeden Fehler verschluckt, versteckt Bugs. Brauchen Sie den Fehler nicht, schreiben Sie catch { … } ohne Klammern.

finally läuft immer

finally { … } läuft nach dem try-Block und, falls einer lief, nach dem catch-Block, ob ein Fehler geworfen wurde oder nicht, und sogar dann, wenn try oder catch ein return ausführt. Es ist der Ort für Aufräumarbeiten: eine Datei schließen, einen Ladehinweis ausblenden, einen Button wieder freigeben. Der Wert eines return in try steht zuerst fest; finally läuft, bevor die Funktion ihn zurückgibt. Verwenden Sie in finally selbst weder return noch throw: Das ersetzt das frühere return oder den früheren Fehler.

Quellen

Zuletzt geprüft am 5. Oktober 2026