Waarom error handling?
Fouten zijn onvermijdelijk: een API is offline, invoer klopt niet, JSON is kapot. Zonder error handling crasht je hele app. Met goede error handling blijft de app werken en zie je wat er misgaat.
- Bij API-aanroepen (server kan offline zijn)
- Bij JSON.parse (data kan kapot zijn)
- Bij localStorage (kan vol zijn of geblokkeerd)
- Bij het valideren van gebruikersinvoer
- Bij code die onverwachte invoer kan ontvangen
Zonder error handling
const data = JSON.parse('dit is geen JSON');
console.log(data.naam); // deze regel wordt nooit bereikt
// De app crasht volledig op de regel hierboven
try / catch
Met try/catch probeer je een stuk code uit te voeren. Als er een fout optreedt, vangt catch die op zonder dat de app crasht.
try {
const data = JSON.parse('dit is geen JSON');
console.log(data);
} catch (error) {
console.log('Er ging iets mis:', error.message);
}
// App loopt gewoon door
console.log('Dit wordt nog steeds uitgevoerd');
try { }— de code die je wilt proberencatch (error) { }— wordt uitgevoerd als er een fout iserror— het error object met informatie over de fout
Het error object
try {
null.naam; // eigenschap opvragen van null → fout
} catch (error) {
console.log(error.name); // "TypeError"
console.log(error.message); // "Cannot read properties of null"
console.log(error.stack); // volledige foutmelding met regelnummer
}
Praktisch voorbeeld — veilig JSON lezen
function veiligParseren(jsonString) {
try {
return JSON.parse(jsonString);
} catch (error) {
console.error('Ongeldige JSON:', error.message);
return null;
}
}
const geldig = veiligParseren('{"naam":"Jan"}');
console.log(geldig.naam); // "Jan"
const ongeldig = veiligParseren('dit is kapot');
console.log(ongeldig); // null
finally
finally wordt altijd uitgevoerd — of er nu een fout was of niet. Handig voor opruimwerk.
function laadData() {
toonLaadSpinner();
try {
const data = haalDataOp();
toonData(data);
} catch (error) {
toonFoutmelding(error.message);
} finally {
verbergLaadSpinner(); // wordt altijd uitgevoerd
}
}
Praktisch voorbeeld
const laadKnop = document.querySelector('#laadKnop');
async function laadGebruikers() {
laadKnop.disabled = true;
laadKnop.textContent = 'Laden...';
try {
const response = await fetch('/api/gebruikers');
const data = await response.json();
toonGebruikers(data);
} catch (error) {
toonFout('Kon gebruikers niet laden');
} finally {
laadKnop.disabled = false;
laadKnop.textContent = 'Laad opnieuw';
}
}
throw
Met throw gooi je zelf een fout. Dit is handig als je eigen validatiefouten wilt maken.
function deelDoor(a, b) {
if (b === 0) {
throw new Error('Je kunt niet door nul delen');
}
return a / b;
}
try {
console.log(deelDoor(10, 2)); // 5
console.log(deelDoor(10, 0)); // gooit een fout
} catch (error) {
console.log(error.message); // "Je kunt niet door nul delen"
}
Validatie met throw
function valideerLeeftijd(leeftijd) {
if (typeof leeftijd !== 'number') {
throw new TypeError('Leeftijd moet een getal zijn');
}
if (leeftijd < 0 || leeftijd > 150) {
throw new RangeError('Leeftijd moet tussen 0 en 150 zijn');
}
return true;
}
try {
valideerLeeftijd('twintig');
} catch (error) {
console.log(`${error.name}: ${error.message}`);
// "TypeError: Leeftijd moet een getal zijn"
}
Error types
JavaScript heeft verschillende ingebouwde error types. Je kunt ze gebruiken om specifieker te zijn over wat er misging.
| Type | Wanneer | Voorbeeld |
|---|---|---|
Error |
Algemene fout | new Error('Iets ging mis') |
TypeError |
Verkeerd type | null.naam |
RangeError |
Waarde buiten bereik | new Array(-1) |
ReferenceError |
Variabele niet gevonden | console.log(bestaatNiet) |
SyntaxError |
Ongeldige code of JSON | JSON.parse('kapot') |
Specifiek catch op type
try {
riskanteCode();
} catch (error) {
if (error instanceof TypeError) {
console.log('Verkeerd type:', error.message);
} else if (error instanceof RangeError) {
console.log('Waarde buiten bereik:', error.message);
} else {
console.log('Onbekende fout:', error.message);
}
}
Errors in async code
Bij async/await gebruik je ook try/catch om fouten op te vangen van fetch en andere promises.
async function haalData(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP fout: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Fout bij ophalen data:', error.message);
return null;
}
}
const data = await haalData('https://api.example.com/users');
if (data) {
toonData(data);
}
Met .catch() bij promises
fetch('./data.json')
.then(response => response.json())
.then(data => toonData(data))
.catch(error => {
console.error('Fout:', error.message);
toonFoutmelding('Data kon niet worden geladen');
});
try/catch bij async/await en .catch() bij promise chains. Beide werken, maar mix ze niet door elkaar in dezelfde functie.
Praktische patronen
Veilig localStorage gebruiken
function veiligOpslaan(key, waarde) {
try {
localStorage.setItem(key, JSON.stringify(waarde));
return true;
} catch (error) {
console.error('Opslaan mislukt:', error.message);
return false;
}
}
function veiligOphalen(key, standaard = null) {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : standaard;
} catch (error) {
console.error('Ophalen mislukt:', error.message);
return standaard;
}
}
Formulier validatie met throw
function valideerFormulier(naam, email) {
if (!naam || naam.trim() === '') {
throw new Error('Naam is verplicht');
}
if (!email.includes('@')) {
throw new Error('Ongeldig e-mailadres');
}
return true;
}
const submitKnop = document.querySelector('#submitKnop');
const feedback = document.querySelector('#feedback');
submitKnop.addEventListener('click', () => {
const naam = document.querySelector('#naam').value;
const email = document.querySelector('#email').value;
try {
valideerFormulier(naam, email);
feedback.textContent = 'Formulier verstuurd!';
feedback.className = 'success-box';
} catch (error) {
feedback.textContent = error.message;
feedback.className = 'warning-box';
}
});
Altijd loggen
Gebruik console.error in catch om fouten zichtbaar te houden tijdens ontwikkeling
Zinvolle meldingen
Geef de gebruiker een begrijpelijke foutmelding, niet de technische error
Niet alles wrappen
Gebruik try/catch alleen waar fouten echt kunnen optreden, niet overal
Finally voor opruimen
Gebruik finally voor spinners, knoppen en andere UI die altijd reset moet worden
Samenvatting
| Keyword | Beschrijving |
|---|---|
try { } |
Code die mogelijk een fout veroorzaakt |
catch (e) { } |
Wordt uitgevoerd als er een fout was |
finally { } |
Wordt altijd uitgevoerd, fout of niet |
throw new Error() |
Zelf een fout gooien |
error.message |
Leesbare foutomschrijving |
error.name |
Type fout (TypeError, RangeError, etc.) |