Uanset hvor gode vi er til programmering, så har vores scripts periodisk fejl. De kan opstå på grund af vores fejl, et uventet brugerinput, et fejlbehæftet server svar, og for tusind andre grunde.
Normalt dør et script (stoppes øjeblikkeligt) når en fejl opstår, og fejlen udskrives til konsollen.
Men der er en konstruktion try...catch som tillader os at “fange” fejl, så scriptet kan gøre noget mere fornuftigt end at dø.
Syntaksen “try…catch”
Konstruktionen try...catch har to hovedblokke: try, og derefter catch:
try {
// kode...
} catch (err) {
// fejlhåndtering
}
Det virker sådan:
- Først vil koden i
try {...}blive kørt. - Hvis der ikke er nogen fejl, så ignoreres
catch (err): eksekveringen når til slutningen aftryog går videre, springer overcatch. - Hvis en fejl opstår, så stoppes
try-eksekveringen, og kontrol flyttes til begyndelsen afcatch (err). Denerr-variabel (vi kan bruge et hvilket som helst navn) vil indeholde et fejlobjekt med detaljer om, hvad der skete.
Så, hvis der sker en fejl inde i try {...} blokken, så dør scriptet ikke – vi har en chance til at håndtere den i catch.
Lad os se på et par eksempler.
-
Et eksempel uden fejl: viser
alert(1)og(2):try { alert('Start på "try runs"'); // (1) <-- // ...ingen fejl her alert('Slut på "try runs"'); // (2) <-- } catch (err) { alert('Catch ignoreres fordi der ikke var fejl'); // (3) } -
Et eksempel med en fejl: viser
(1)og(3):try { alert('Start på "try runs"'); // (1) <-- lalala; // fejl, variable is not defined! alert('Slut på "try runs"'); // (2) } catch (err) { alert(`Der skete en fejl!`); // (3) <-- }
try...catch virker kun for runtime errorsFor at try...catch skal virke skal koden kunne køre. Med anre ord skal det være gyldig (valid) JavaScript.
Den virker ikke hvis syntaksen er forkert. For eksempel hvis der er rod med parenteser:
try {
{{{{{{{{{{{{
} catch (err) {
alert("Motoren forstår ikke denne kode, den er ugyldig");
}
JavaScript-motoren læser koden først, og derefter kører den. De fejl, der opstår under læsningen, kaldes “parse-time” fejl og er unrecoverable (fra inde i koden). Det er fordi motoren ikke kan forstå koden.
Så, try...catch kan kun håndtere fejl, der opstår i gyldig kode. Sådanne fejl kaldes “runtime errors” eller, nogle gange, “exceptions”.
````warn header="`try...catch` virker synkront"
Hvis en exception opstår i "planlagt" kode, som i `setTimeout`, så vil `try...catch` ikke fange den:
```js run
try {
setTimeout(function() {
noSuchVariable; // script will die here
}, 1000);
} catch (err) {
alert( "det her virker ikke" );
}
Det er fordi at selve funktionen først afvikles senere – det har motoren allerede forladt try...catch konstruktionen.
For at fange en exception inde i en planlagt funktion, skal try...catch være inde i selve funktionen:
setTimeout(function() {
try {
noSuchVariable; // try...catch håndterer fejlen!
} catch {
alert( "fejlen fanges her!" );
}
}, 1000);
Error objekt
Når der opstår en fejl, genererer JavaScript et objekt, der indeholder detaljerne om fejlen. Objektet sendes derefter som et argument til catch:
try {
// ...
} catch (err) { // <-- her er "error objektet", du kan sagtens bruge et andet navn istedet for err
// ...
}
For alle indbyggede fejl har error objektet to hovedegenskaber:
name- Navnet på fejlen. Hvis det for eksempel er en ikke-defineret variabel, så indeholder den
"ReferenceError". message- En tekst der beskriver fejlen.
Der er andre ikke-standard egenskaber tilgængelige i de fleste miljøer. En af de mest brugte og understøttede er:
stack- Aktuel kaldestak: en streng med information om sekvensen af indlejrede kald, der ledte til fejlen. Bruges til fejlfinding.
For eksempel:
try {
lalala; // fejl, variablen er ikke defineret!
} catch (err) {
alert(err.name); // ReferenceError
alert(err.message); // lalala is not defined
alert(err.stack); // ReferenceError: lalala is not defined at (...call stack)
// Kan også vise en fejl som et samlet output
// Fejlen konverteres til en streng som "name: message"
alert(err); // ReferenceError: lalala is not defined
}
Frivillig “catch” binding
Hvis vi ikke behøver detaljer om fejlen, kan catch udelade den:
try {
// ...
} catch { // <-- uden (err)
// ...
}
Brug af “try…catch”
Lad os udforske brugsscenarier for try...catch.
Som vi allerede ved så understøtter JavaScript metoden JSON.parse(str) til at læse JSON værdier.
Normalt bruges den til at afkode data der er modtaget over netværket, fra serveren eller anden kilde.
Vi modtager det og kalder JSON.parse sådan her:
let json = '{"name":"John", "age": 30}'; // data from the server
let user = JSON.parse(json); // konverterer tekstrepræsentation til JS-objekt
// nu er user et objekt med egenskaber fra strengen
alert( user.name ); // John
alert( user.age ); // 30
Du kan finde mere detaljeret information om JSON i kapitlet JSON metoder, toJSON.
Hvis json indeholder fejl vil JSON.parse generere en fejl, så scriptet “dør”.
Skal vi stille os tilfreds med det? Nej, selvfølgelig ikke!
På denne måde, hvis der er noget galt med data, vil besøgende aldrig vide det (medmindre de åbner konsollen). Og folk kan ikke li’, når noget “bare dør” uden nogen fejlbesked.
Lad os bruge try...catch til at håndtere fejlen:
let json = "{ bad json }";
try {
let user = JSON.parse(json); // <-- når en fejl opstår...
alert( user.name ); // virker ikke
} catch (err) {
// ...hopper afviklingen her til
alert( "Desværre indeholder vores data fejl, vi vil forsøge at anmode om den en gang til." );
alert( err.name );
alert( err.message );
}
Her bruger vi kun catch blokken til at vise en besked, men vi kan gøre meget andet: sende et nyt netværkskald, foreslå et alternativ til besøgende, send information om fejlen til en log, … alle muligheder der er meget bedre end bare at dø.
Kaste vores egne fejl
Hvad hvis json er syntactisk korrekt, men ikke har en påkrævet name egenskab?
Som dette:
let json = '{ "age": 30 }'; // ufuldstændige data
try {
let user = JSON.parse(json); // <-- ingen fejl
alert( user.name ); // intet navn!
} catch (err) {
alert( "afvikles ikke" );
}
Her kører JSON.parse normalt, men fraværet af