Coroutines — kernconcepten, Job en Dispatchers in Kotlin

Auteur: IT Sectr Gepubliceerd: 2026-03-16 Leestijd: 8 min

Coroutines (coroutines) zijn lichte Kotlin-threads voor asynchroon programmeren, beschikbaar via de bibliotheek kotlinx.coroutines. Volgens JetBrains Kotlin Documentation, 2026 stellen Coroutines je in staat de uitvoering van een functie te onderbreken zonder de thread te blokkeren, in tegenstelling tot traditionele Threads. Coroutines worden uitgevoerd op een beperkte pool van threads, waardoor ze duizend keer lichter zijn dan native threads. Kotlin Coroutines zijn volledig geïntegreerd met Android Jetpack, Retrofit, Room en andere populaire bibliotheken in het Android-ecosysteem.

Belangrijkste punten

  • Coroutines — lichte Kotlin-threads voor asynchrone code zonder blokkering
  • Suspend-functie — functie die kan worden onderbroken en hervat zonder de thread te blokkeren
  • Dispatcher bepaalt de threadpool voor het uitvoeren van de coroutine
  • Job — descriptor van de coroutine met ondersteuning voor annulering en statusregistratie
  • CoroutineScope beheert de levenscyclus van coroutines en hun annulering bij voltooiing

Wat zijn Kotlin-coroutines

Coroutines (coroutines) zijn een asynchroon programmeermechanisme in Kotlin, geïmplementeerd in de bibliotheek kotlinx.coroutines. In tegenstelling tot threads van het besturingssysteem, zijn coroutines niet gebonden aan een specifieke thread: ze kunnen worden onderbroken op de ene thread en hervat op een andere. Eén thread kan duizenden coroutines uitvoeren door met minimale overhead tussen ze te schakelen.

Coroutines verschenen in Kotlin 1.3 (2018) als experimentele functie en werden stabiel in Kotlin 1.5 (2021). Coroutines lossen het callback-hell-probleem op, vergelijkbaar met async/await, maar bieden een rijker API: kanalen (Channel), Flow, uitzonderingsafhandeling in de Job-hiërarchie en directe integratie met Android Lifecycle.

Volgens JetBrains (2025) verbruikt elke coroutine ongeveer 100 bytes geheugen tegenover 1+ MB voor een native thread. Dit maakt het mogelijk miljoenen coroutines in één applicatie uit te voeren zonder risico op OutOfMemoryError. Juist de lichtheid van coroutines maakt ze tot het voorkeursinstrument voor asynchroniteit in Android.

Hoe coroutines onder de motorkap werken

Elke Kotlin-coroutine wordt gecompileerd naar een toestandsmachine via Continuation Passing Style (CPS). De compiler voegt een verborgen parameter Continuation toe aan elke suspend-functie. Continuation bevat het hervattingspunt en alle lokale variabelen. Wanneer de coroutine wordt onderbroken, slaat de runtime de Continuation op, en wanneer deze wordt hervat, wordt deze hersteld op elke beschikbare thread uit de Dispatcher-pool.

Suspend-functies: onderbreken en hervatten

suspend — het Kotlin-sleutelwoord dat een functie markeert als onderbreekbaar. Zo'n functie kan alleen worden aangeroepen vanuit een andere suspend-functie of vanuit een coroutine. Binnen een suspend-functie kunnen andere suspend-functies in willekeurige volgorde worden aangeroepen, en elk aanroeppunt is een potentieel onderbrekingspunt.

De mechaniek is eenvoudig: wanneer een suspend-functie een andere suspend-functie aanroept, onderbreekt deze op dat punt, waarbij de thread vrijkomt. Na voltooiing van de aangeroepen functie zet de runtime de uitvoering voort vanaf de opgeslagen locatie. Dit wordt cooperative cancellation genoemd — geen enkele thread wordt geblokkeerd.

  • Onderbreking — de coroutine geeft de thread vrij zonder deze te blokkeren
  • Hervatting — de coroutine gaat verder vanaf dezelfde plaats waar deze werd onderbroken
  • Thread — de coroutine kan worden onderbroken op thread A en hervat op thread B
  • Uitzonderingen — worden afgehandeld via try/catch, net als in synchrone code

Belangrijk: een suspend-functie is standaard niet asynchroon. De uitvoeringsvolgorde blijft sequentieel tenzij launch of async wordt gebruikt. suspend stelt de functie alleen in staat te worden onderbroken zonder de thread te blokkeren en deel uit te maken van de coroutine-context. Continuation Passing Style is een compilatiemodel waarbij elke suspend-functie een verborgen callback Continuation ontvangt en de compiler een toestandsmachine genereert voor het beheren van onderbrekingen en hervattingen.

CoroutineScope en Structured Concurrency

CoroutineScope is een context die de levenscyclus van coroutines definieert. Alle coroutines moeten binnen een scope worden gestart. Wanneer de scope wordt geannuleerd (bijvoorbeeld bij het beëindigen van een Activity), worden alle onderliggende coroutines automatisch geannuleerd. Dit voorkomt het weglekken van achtergrondtaken. Android Jetpack biedt kant-en-klare scopes voor elke component: viewModelScope voor ViewModel en lifecycleScope voor Activity en Fragment, die automatisch worden geannuleerd bij vernietiging van de betreffende component.

Structured Concurrency — het principe dat garandeert dat een coroutine niet wordt voltooid voordat alle onderliggende coroutines zijn voltooid. De Job-hiërarchie vormt een boom: de root-coroutine maakt een parent job aan, kinderen maken child jobs. Annulering van de parent job verspreidt zich naar alle kinderen. Structured Concurrency is het fundamentele verschil tussen coroutines en threads.

ScopeWaar gebruiktAnnulering
GlobalScopeAlleen voor daemon-takenWordt niet automatisch geannuleerd
viewModelScopeAndroid ViewModelBij het opschonen van ViewModel
lifecycleScopeAndroid Activity/FragmentBij vernietiging van lifecycle
coroutineScopeBinnen een suspend-functieBij annulering van de parent job

SupervisorJob voor foutafhandeling

Een gewone Job annuleert bij een fout in een onderliggende coroutine alle siblings. SupervisorJob is een uitzondering: een fout in één onderliggende coroutine heeft geen invloed op de rest. Dit is belangrijk wanneer meerdere onafhankelijke taken parallel worden uitgevoerd en één kan falen zonder dat de anderen hoeven te worden geannuleerd.

Dispatchers en coroutine-builders

Dispatchers bepalen op welke threads coroutines worden uitgevoerd. Dispatchers.Main — de hoofd-UI-thread van Android. Dispatchers.IO — pool voor blokkerende operaties (netwerk, schijf). Dispatchers.Default — voor CPU-intensieve taken. Dispatchers.Unconfined — start op de huidige thread maar garandeert niet dat deze erop blijft. De juiste Dispatcher kiezen is cruciaal voor prestaties: een IO-taak op Default blokkeert de pool voor berekeningen, terwijl een CPU-taak op IO onnodige threads creëert.

withContext — functie voor het wisselen van Dispatcher binnen een coroutine. Een suspend-functie die JSON parsed kan bijvoorbeeld overschakelen naar Dispatchers.Default tijdens de berekening en terugkeren naar Dispatchers.Main voor UI-updates. withContext is de meest gebruikte builder in Android-ontwikkeling.

Drie belangrijkste coroutine-builders

launch — start een coroutine, retourneert Job, retourneert geen resultaat (fire-and-forget). async — start een coroutine, retourneert Deferred waaruit het resultaat kan worden verkregen via await. runBlocking — blokkeert de huidige thread voor het uitvoeren van de coroutine (alleen voor tests en main-functies). Builder-keuze hangt af van het scenario: launch is geschikt voor gebeurtenissen en updates, async voor taken met resultaat, runBlocking alleen voor tests of ingangspunten.

Codevoorbeelden met coroutines in Kotlin

Laten we drie praktische scenario's bekijken: basis-coroutine met launch, parallelle aanroep met async en foutafhandeling met SupervisorJob.

Coroutine starten met launch

viewModelScope.launch start een coroutine in de context van ViewModel. Bij het opschonen van ViewModel wordt de coroutine automatisch geannuleerd.

kotlin
class ProfileViewModel : ViewModel() {
    fun loadUser() {
        viewModelScope.launch(Dispatchers.IO) {
            val user = api.fetchUser()
            withContext(Dispatchers.Main) {
                showUser(user)
            }
        }
    }
}

Parallelle verzoeken met async

coroutineScope met async start drie verzoeken parallel. Resultaten worden verzameld via .await(). Als een van de verzoeken faalt, worden alle geannuleerd.

kotlin
suspend fun loadDashboard(): Dashboard = coroutineScope {
    val user = async { api.fetchUser() }
    val posts = async { api.fetchPosts() }
    val stats = async { api.fetchStats() }
    Dashboard(user.await(), posts.await(), stats.await())
}

Foutafhandeling met SupervisorJob

SupervisorJob laat elke coroutine onafhankelijk voltooien. Een fout in één verzoek annuleert de andere niet.

kotlin
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
    try { api.fetchUsers() } catch (e: Exception) { log(e) }
}
scope.launch {
    try { api.fetchPosts() } catch (e: Exception) { log(e) }
}

Coroutines vs threads: vergelijking en scenario's

Threads — primitief van het besturingssysteem. Elke thread heeft een eigen stack (~1 MB) en vereist een systeemaanroep voor creatie en schakeling. Coroutines — taalprimitief, niet gebonden aan het OS. Ze gebruiken Continuation voor het opslaan van de toestand en schakelen op runtim niveau zonder systeemaanroepen.

  • Geheugen — thread ~1 MB, coroutine ~100 bytes. Verschil van 10 000 keer
  • Creatie — thread ~1 µs syscall, coroutine ~0.01 µs op JVM-niveau
  • Schakeling — thread ~0.1 µs (syscall), coroutine ~0.001 µs (continuation)
  • Maximum — duizenden threads vs miljoenen coroutines op een apparaat
  • Annulering — thread kan niet van buitenaf worden geannuleerd (verouderde Thread.stop), coroutine kan via Job.cancel()

Volgens Google (2025) vermindert het gebruik van coroutines in plaats van threads het geheugengebruik voor achtergrondtaken in Android-applicaties met 90–95%. Alle moderne Android-bibliotheken (Retrofit, Room, WorkManager) hebben ingebouwde ondersteuning voor coroutines via suspend-functies. Ktor (het HTTP-clientframework van JetBrains) is ook volledig gebouwd op coroutines en biedt voor elk verzoek suspend-functies zonder callback-API. Room ondersteunt coroutines via suspend-functies in DAO, waardoor databasequery's kunnen worden uitgevoerd zonder de hoofdthread te blokkeren.

Wanneer threads gebruiken in plaats van coroutines

Threads blijven noodzakelijk voor native code via JNI, blokkerende CPU-intensieve aanroepen zonder tijdslimiet (videorendering, simulaties) en bij integratie met C-bibliotheken. Voor al het andere — coroutines.

Veelgestelde vragen

Wat is het verschil tussen een coroutine en een thread?

Coroutine — een onderbreekbare werkeenheid die wordt uitgevoerd op een bestaande thread. Een thread is een systeembron met een eigen stack. Coroutines zijn duizenden keren lichter dan threads en blokkeren geen bronnen bij onderbreking.

Wat is Dispatchers.IO en waarin verschilt het van Default?

Dispatchers.IO is bedoeld voor blokkerende I/O-operaties (netwerk, bestanden) en kan indien nodig nieuwe threads maken. Dispatchers.Default heeft een vaste pool (aantal CPU-kernen) voor CPU-intensieve berekeningen.

Hoe annuleer ik een gestarte coroutine?

Job.cancel() annuleert de coroutine en al zijn kinderen. Gebruik ensureActive() om annulering binnen de coroutine te controleren — dit gooit CancellationException als de coroutine is geannuleerd.

Kan ik coroutines gebruiken met RxJava?

Ja — via de bibliotheek kotlinx-coroutines-rx3. Het biedt functies awaitSingle, awaitFirst en andere voor het converteren van Observable/Single naar suspend-functies en terug via flowable.

Wat is Flow in coroutines?

Flow — een koude asynchrone gegevensstroom, de tegenhanger van RxJava Observable voor coroutines. Flow zendt waarden sequentieel uit en eindigt met een uitzondering of succes. Ondersteunt map, filter, catch en andere operatoren.

Samenvatting

  • Coroutines — lichte Kotlin-threads met onderbreking zonder blokkering via Continuation Passing Style
  • suspend — sleutelwoord voor het markeren van onderbreekbare functies
  • Dispatchers beheren de threadpool: Main, IO, Default respectievelijk
  • CoroutineScope koppelt de levenscyclus van coroutines aan de component (Activity, ViewModel)
  • launch start een coroutine zonder resultaat, async/await — met resultaat
  • Structured Concurrency garandeert hiërarchische annulering van onderliggende coroutines
  • Coroutines vs threads — coroutines zijn 10 000 keer lichter en zijn de standaard voor Android

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook