Fehlerbehandlung

Es gibt 2 Arten von Fehlern Syntaxfehler und Laufzeitfehler. Syntaxfehler sind Fehler, wo man etwas falsch geschrieben hat, beipsielsweise funktion anstatt function, oder wenn man eine Klammer vergessen hat. Diese Fehler werden in Visual Studio Code angezeigt, oder zur Laufzeit in der Konsole.

Laufzeitfehler sind Fehler, die zur Laufzeit auftreten, also wenn das Programm ausgeführt wird. Eigentlich fallen in Javascript auch die Syntaxfehler in diese Kategorie, weil die ja auch erst zur Laufzeit auftreten. Der Unterschied ist jedoch, die Laufzeitfehler treten erst dann auf, wenn das Stück Code auch interpretiert wird. Beispielsweise wenn eine Funktion aufgerufen wird, in der sich der Fehler befindet.

Logische Fehler sind schwieriger zu finden, denn das sind Fehler, wo die Programmierung nicht richtig durchdacht ist und daher ein Fehler nur in bestimmten Situationen vorkommt. Beispielsweise, dass man auf ein Arrayelement zugreift, welches nicht vorhanden ist oder, dass man eine Zahl durch Null teilt.

Fehler nennt man in Programmiersprachen auch Ausnahmen und Fehlerbehandlung wird somit auch Ausnahmebehandlung genannt.

try catch

Tritt ein Laufzeitfehler auf, man nennt es auch wenn ein Fehler geworfen wird, gibt es in Javascript die Möglichkeit mit der sogenannten Fehlerbehandlung darauf zu reagieren. Man nennt es auch einen Fehler zu fangen und diesen zu behandeln.

Man kann Fehler über try-catch Codeblöcke abfangen und zusätzlich über den Event Handler window.onerror darauf reagieren.

Im try Codeblock wird ein Codeblock ausgeführt, der einen Fehler produzieren könnte.

Im catch Codeblock lassen sich Fehler fangen, um darauf zu reagieren. Im catch Codeblock definiert man einen selbstvergebenen Namen für das Fehlerobjekt in runden Klammern auf dass man anschließend im catch Codeblock zugreifen kann. Jedem try Block kann nur ein catch Block zugewiesen werden.

try{
    //code mit potentiellen Fehlern ausführen
}catch(error){
    //Behandeln des Fehlers
console.log(error.name);
console.log(error.message);
}

Das Fehlerobjekt hat die Eigenschaften name und message, die man im catch Block aufrufen kann.

Es gibt 6 Fehlertypen, die vom Basistyp Error ableiten

Siehe folgendes Beispiel und gebe eine negative Zahl ein

    let eingabe = prompt( "Bitte geben Sie die Länge des Arrays ein." );
    let laenge = parseInt( eingabe );
    let arr;
    try
    {
        arr = createArray( laenge );

    } catch ( error )
    {
        console.log( error.name );
        console.log( error.message );
    }

    function createArray ( lang )
    {
        return new Array( lang );
    }

Im vorigen Beispiel wird bei der Eingabe einer negativen Zahl der error.name "rangeError" und die error.message "invalid array length" ausgegeben.

throw Fehler auslösen

Man kann auch einen Fehler werfen. In folgedem Beispiel wird bei der Eingabe einer netativen Zahl ein Fehler geworfen. Die Anweisungen nach dem Fehler werden nicht mehr ausgeführt.

siehe Beispiel im Devloper Modus

    console.log( checkPrice( 20 ) );
    console.log( checkPrice( -3 ) );
    console.log( "nach dem Fehler" );
    function checkPrice ( price )
    {
        if ( price < 0 )
        {
            throw new Error( "Preis darf nicht negativ sein" );
        }
    }

Man kann auch mehrere Fehler werfen, wie im folgenden Beispiel. Je nach Art des Fehlers sollte man auch die Art des Errors definieren. Hier wurde ein TypeError und RangeError definiert.

siehe Beispiel im Developer Modus

function checkPrice ( price )
{
    if ( isNaN( parseFloat( price ) ) )
    {
        throw new TypeError( "Preis muss eine Zahl sein" );
    } else if ( price < 0 )
    {
        throw new RangeError( "Preis muss größer als 0 sein." )
    } else
    {
        console.log( "Der Preis ist " + price );
    }
}

Im vorigen Beispiel wurde die Funktion checkPrice() mittels User-Interaktion aufgerufen. Würde man stattdessen, so wie zuvor mehrere Funktionsaufrufe direkt hintereinander aufführen, würde nach dem ersten Fehler keine weitere Funktion ausgeführt werden. Sie werden vom Interpreter ignoriert. Hier müsste man dann try-catch-Blöcke einsetzen.

 

siehe Beispiel

try { console.log( checkPrice( 20 ) ); } catch ( error )
{
    console.log( error.name );
    console.log( error.message );
}
try { console.log( checkPrice( -3 ) ); }
catch ( error )
{
    console.log( error.name );
    console.log( error.message );
}
try { console.log( checkPrice( "vier" ) ); } catch ( error )
{
    console.log( error.name );
    console.log( error.message );
}


function checkPrice ( price )
{
    if ( isNaN( parseFloat( price ) ) )
    {
        throw new TypeError( "Preis muss eine Zahl sein" );
    } else if ( price < 0 )
    {
        throw new RangeError( "Preis muss größer als 0 sein." )
    } else
    {
        console.log( "Der Preis ist " + price );
    }
}

Funktionsaufrufstack und Fehler

Wird ein Fehler in der Funktion, welche den Fehler wirft, nicht gefangen, springt der Interpreter aus der Funktion heraus. Wird der Fehler in der nächsten Funktion, besser gesagt im Funktionsaufrufstack nicht gefangen, springt der Interpreter auch hier heraus. Wenn der Fehler überhaupt nicht gefangen wird, landet er beim Nutzer in der Konsole.

siehe Beispiel

function machWas ()
{
    try
    {
        machWas2();
    } catch ( error )
    {
        console.log( error.message );
    }
}

function machWas2 ()
{
    machWas3();
}

function machWas3 ()
{
    throw new Error( "Fehler in mach3" );
}

machWas();

Lese diese Grafik von links nach rechts. Der Fehler wird in machWas3() geworfen aber nicht aufgefangen. Der Interpreter geht in machWas2() und von dort in machWas() wo der Fehler gefangen wird.

machWas3
throw new Error
->
machWas2{
machWas3}
->
machWas{
try{machWas2}
catch(error)
{Fehlerbehandlung }}

Beispiel in der Praxis

Im folgenden Beispiel wird die User-Eingabe für ein Alter auf verschiedene Eingaben überprüft und dementsprechend eine Fehlermeldung ausgegeben. Alter unter 0, Alter über 110 oder eine Zeichenkette wird mit einer entsprechenden Ausgabe versehen.

siehe Beispiel

HTML

<input type="text" id="alter">
<p><button onclick="checkEingabe()">Check</button></p>
<p id="ausgabe"></p>

Script

function checkEingabe ()
{
    let age = document.getElementById( "alter" ).value;
    console.log( age );
    try
    {
        checkAge( parseFloat( age ) );
    } catch ( error )
    {
        document.getElementById( "ausgabe" ).textContent = error.message;
        return;
    }
    document.getElementById( "ausgabe" ).textContent = "Alt genug";


}
function checkAge ( age )
{
    if ( parseFloat( age ) < 0 )
    {
        throw new Error( "Alter darf nicht negativ sein" );
    } else if ( isNaN( parseFloat( age ) ) )
    {
        throw new TypeError( "Geben Sie eine Zahl ein." );
    } else if ( parseFloat( age ) > 110 )
    {
        throw new Error( "So alt wird kein Schwein." );
    }
}
  

try catch finally

In einer try catch Anweisung kann man noch ein finally hinten anfügen. Die dort eingefügten Anweisungen werden auch ausgeführt, wenn zuvor ein Fehler auftrat. Das kann man beispielsweise bei Datenbankzugriffen gebrauchen. Bei Programmierungen mit Datenbanken braucht man mehrere Schritte, öffnen der Datenbank, Zugriff auf die Daten, Schließen der Datenbank.

In folgendem Beispiel habe ich für jeden Schritt eine Beispielfunktion definiert. In der Funktion accessDatabase() werden diese 3 Funktionen der Reihe nach aufgerufen. Damit das Beispiel funktioniert habe ich oben eine Select Auswahl eingefügt, wo man sich einen Fehler aussuchen kann. Wurde der Fehler "verbindung" gewählt, wird in der Funktion openDatabase() ein error geworfen. Wird der Fehler "zugriff" gewählt wird ein Fehler in der Funktion getData() geworfen. Bei Arbeiten mit Datenbanken müssen die Datenbanken auf jeden Fall geschlossen werden, das wird hier durch die Funktion closeDatabase() veranschaulicht.

Im folgenden Beispiel7 gibt es das Problem, dass closeDatabase() bei einem Fehler nicht aufgerufen wird. Im Beispiel8 wurde finally eingesetzt, so dass auf jeden Fall die closeDatabase() auch bei einem Fehler aufgerufen wird.

beispiel7 / beispiel8

HTML

<p><label><select name="fehler" id="fehler">
<option value="">kein Fehler</option>
<option value="verbindung">Verbindung</option>
<option value="zugriff">Zugriff</option>
</select> Wählen Sie einen Fehler</label></p>
<p id="ausgabe"></p>
Script
   
    let fehler;
    let fehlerEingabe = document.getElementById( "fehler" );
    let ausgabe = document.getElementById( "ausgabe" );
    fehlerEingabe.addEventListener( "change", checkEingabe );
    function checkEingabe ()
    {
        fehler = fehlerEingabe.value;
        ausgabe.innerHTML = "";

        try
        {
            accessDatabase();
        } catch ( error )
        {
            ausgabe.innerHTML = error.message;
        } finally
        {
            closeDatabase();
        }

    }

    function openDatabase ()
    {
        if ( fehler == "verbindung" )
        {
            throw new Error( "Fehler bei der Verbindung" );
        }
    }

    function getData ()
    {
        if ( fehler == "zugriff" )
        {
            throw new Error( "Fehler beim Zugriff der Daten" );
        }
    }
    function closeDatabase ()
    {
        ausgabe.innerHTML += "
Datenbank wurde geschlossen"; } function accessDatabase () { openDatabase(); getData(); }

Javascript Tipps