Wat is Async Code?

Asynchrone code is code die niet meteen klaar is. Denk aan:

  • Data ophalen van een API
  • Bestanden inladen
  • Wachten op gebruikersinput
Belangrijk: JavaScript wacht niet automatisch. Het gaat door naar de volgende regel, ook als de vorige nog bezig is.

Voorbeeld van het gedrag

console.log('1. Start');

// Simuleer een API call van 2 seconden
setTimeout(() => {
  console.log('2. Data opgehaald');
}, 2000);

console.log('3. Einde');

// Output:
// 1. Start
// 3. Einde
// 2. Data opgehaald (2 seconden later)
Wat gebeurt er?
  • JavaScript wacht NIET op setTimeout
  • Regel 3 wordt meteen uitgevoerd
  • Pas na 2 seconden komt de setTimeout callback

Het probleem met async code

Je wilt data ophalen en daarna iets ermee doen, maar JavaScript is al verder gegaan.

Probleem voorbeeld

let gebruiker = null;

// Haal gebruiker op (duurt 2 seconden)
setTimeout(() => {
  gebruiker = { naam: 'Jan', leeftijd: 25 };
}, 2000);

// Probeer gebruiker te tonen:
console.log(gebruiker.naam); // ❌ ERROR - gebruiker is nog null
Probleem: De code probeert gebruiker.naam te lezen voordat de data geladen is.

Waarom is dit lastig?

Bij fetch(), API calls en database queries weet je niet precies hoelang het duurt. Het kan 100ms zijn, maar ook 5 seconden.

De oplossing: Promises en Async/Await zorgen ervoor dat je netjes kunt wachten tot de data klaar is.

Promises

Een Promise is een “belofte” dat er later een resultaat komt. Een Promise heeft 3 states:

  • pending – bezig / nog niet klaar
  • fulfilled – gelukt (resultaat beschikbaar)
  • rejected – mislukt (error)

Promise met .then()

fetch('./data.json')
  .then(response => response.json()) // wacht op response
  .then(data => {                    // wacht op JSON
    console.log(data);               // nu is data er
  })
  .catch(error => {                  // als er iets misgaat
    console.error(error);
  });
Wat gebeurt er?
  • fetch() geeft een Promise terug
  • .then() wordt uitgevoerd als de Promise klaar is
  • Je kunt meerdere .then() achter elkaar gebruiken
  • .catch() vangt fouten op

Callback hell

Met alleen .then() kan je code erg genest en onleesbaar worden.

fetch('./users.json')
  .then(r => r.json())
  .then(users => {
    fetch('./posts.json')
      .then(r => r.json())
      .then(posts => {
        fetch('./comments.json')
          .then(r => r.json())
          .then(comments => {
            // heel veel nesting...
          });
      });
  });
Te veel geneste callbacks = onleesbare code. Async/await lost dit op.

Async/Await

Async/await is een moderne manier om met Promises te werken. Het lijkt op “normale” synchrone code.

Voordelen:
  • Leesbaarder
  • Minder nesting
  • Makkelijker te debuggen

Basis syntax

// 1. async functie
async function haalDataOp() {

  // 2. wacht op fetch
  const response = await fetch('./data.json');

  // 3. wacht op het omzetten naar JSON
  const data = await response.json();

  console.log(data); // nu is data er
}

// 4. functie aanroepen
haalDataOp();
Regels:
  • async voor de functie => deze functie kan await gebruiken
  • await => wacht tot de Promise klaar is
  • Je mag await alleen binnen een async functie gebruiken

.then() versus async/await

Met .then():

function haalDataOp() {
  fetch('./data.json')
    .then(response => response.json())
    .then(data => {
      console.log(data);
    });
}

Met async/await:

async function haalDataOp() {
  const response = await fetch('./data.json');
  const data = await response.json();
  console.log(data);
}
Async/await leest als gewone, stap-voor-stap code.

Arrow functie met async

const haalDataOp = async () => {
  const response = await fetch('./data.json');
  const data = await response.json();
  console.log(data);
};

haalDataOp();

Error handling met try/catch

Bij async/await gebruik je try/catch om fouten af te vangen.

async function haalDataOp() {
  try {
    const response = await fetch('./data.json');

    if (!response.ok) {
      throw new Error('Data niet gevonden');
    }

    const data = await response.json();
    console.log(data);

  } catch (error) {
    console.error('Er ging iets mis:', error);
  }
}

haalDataOp();
Wat gebeurt er?
  • try – probeer deze code uit te voeren
  • catch – als er een error is, kom je hier terecht
  • throw new Error() – zelf een error gooien

.then() versus async/await error handling

Met .then():

fetch('./data.json')
  .then(r => r.json())
  .then(data => console.log(data))
  .catch(error => console.error(error));

Met async/await:

async function haalDataOp() {
  try {
    const response = await fetch('./data.json');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}
Best practice: Gebruik altijd try/catch rond async/await code.

Praktische voorbeelden

1. Gebruiker ophalen en tonen

async function toonGebruiker() {
  try {
    const response = await fetch('./user.json');
    const gebruiker = await response.json();

    document.querySelector('#naam').textContent = gebruiker.naam;
    document.querySelector('#email').textContent = gebruiker.email;

  } catch (error) {
    console.error('Fout bij laden gebruiker:', error);
    document.querySelector('#naam').textContent = 'Fout bij laden';
  }
}

toonGebruiker();

2. Meerdere calls na elkaar (afhankelijk van elkaar)

async function haalAllesOp() {
  try {
    const gebruikerResponse = await fetch('./user.json');
    const gebruiker = await gebruikerResponse.json();

    const postsResponse = await fetch(`./posts/${gebruiker.id}.json`);
    const posts = await postsResponse.json();

    const commentsResponse = await fetch(`./comments/${posts[0].id}.json`);
    const comments = await commentsResponse.json();

    console.log(gebruiker, posts, comments);

  } catch (error) {
    console.error('Error:', error);
  }
}

haalAllesOp();
Dit is veel leesbaarder dan geneste .then() callbacks.

3. Meerdere calls tegelijk met Promise.all()

async function haalAllesTegelijkOp() {
  try {
    const [gebruiker, posts, comments] = await Promise.all([
      fetch('./user.json').then(r => r.json()),
      fetch('./posts.json').then(r => r.json()),
      fetch('./comments.json').then(r => r.json())
    ]);

    console.log('Alles geladen:', gebruiker, posts, comments);

  } catch (error) {
    console.error('Error:', error);
  }
}

haalAllesTegelijkOp();
Promise.all() is sneller als de requests niet van elkaar afhankelijk zijn.

4. Data tonen op de pagina

async function toonProducten() {
  const container = document.querySelector('#producten');

  try {
    container.innerHTML = '<p>Producten laden...</p>';

    const response = await fetch('./producten.json');
    const producten = await response.json();

    let html = '';
    for (let product of producten) {
      html += `
        <div class="product">
          <h3>${product.naam}</h3>
          <p>Prijs: €${product.prijs}</p>
        </div>
      `;
    }

    container.innerHTML = html;

  } catch (error) {
    console.error('Error:', error);
    container.innerHTML = '<p>Fout bij laden van producten</p>';
  }
}

document.addEventListener('DOMContentLoaded', toonProducten);

5. Formulier data versturen

async function verstuurFormulier(event) {
  event.preventDefault();

  const naam = document.querySelector('#naam').value;
  const email = document.querySelector('#email').value;

  try {
    const response = await fetch('https://api.example.com/users', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ naam, email })
    });

    if (!response.ok) {
      throw new Error('Verzenden mislukt');
    }

    const result = await response.json();
    console.log(result);
    alert('Succesvol verstuurd!');

  } catch (error) {
    console.error('Error:', error);
    alert('Er ging iets mis bij het versturen');
  }
}

document.querySelector('#mijnForm')
  .addEventListener('submit', verstuurFormulier);

Samenvatting

Concept Wat doet het? Gebruik je het voor...
async Maakt een functie asynchroon Functies die await gebruiken
await Wacht op een Promise fetch, API calls, timeouts, etc.
try/catch Fouten afvangen Async/await code veilig maken
Promise.all() Meerdere Promises tegelijk Sneller meerdere requests doen

Gebruik async/await

Leesbaarder dan lange .then() chains

Altijd try/catch

Vang fouten netjes op

Check response.ok

Controleer of je request gelukt is

Toon loading state

Laat de gebruiker zien dat er geladen wordt

Gebruik Promise.all

Voor parallelle requests

Log veel

Gebruik console.log om te zien wat er gebeurt

Cheat sheet template

async function mijnFunctie() {
  try {
    const response = await fetch('./data.json');

    if (!response.ok) {
      throw new Error('Fout bij ophalen');
    }

    const data = await response.json();

    // Werk met data...
    console.log(data);

  } catch (error) {
    console.error('Error:', error);
  }
}

mijnFunctie();