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.

Wanneer gebruik je error handling?
  • 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 proberen
  • catch (error) { } — wordt uitgevoerd als er een fout is
  • error — 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');
  });
Gebruik 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.)