Async/Await is een paar trefwoorden voor het schrijven van asynchrone code in synchrone stijl, beschikbaar in Swift vanaf iOS 13 en in Kotlin met coroutines. Volgens Apple Swift Documentation, 2026 vervangt Async/Await callback-ketens en GCD, waardoor asynchrone code lineair en leesbaar wordt. Het trefwoord async markeert een functie als asynchroon, en await onderbreekt de uitvoering ervan totdat het resultaat is verkregen.
Belangrijkste punten
Async/Await is een taalconstructie waarmee je asynchrone code lineair kunt schrijven, net als synchrone code. In Swift is het beschikbaar vanaf iOS 13 / macOS 10.15 samen met het Swift Concurrency-framework. In Kotlin is async/await beschikbaar via de coroutine-bibliotheek (kotlinx.coroutines) vanaf versie 1.3.
Vóór async/await werd asynchrone code gebouwd met callbacks, GCD of RxSwift. Geneste callback-ketens leidden tot callback hell — diepe nesting die lezen en debuggen bemoeilijkt. Async/Await lost dit probleem op door asynchrone functies te laten "onderbreken" bij await-punten en te hervatten nadat het resultaat is verkregen.
Volgens Google (2025) vermindert het gebruik van async/await in Kotlin het aantal regels asynchrone code met 40–60% vergeleken met de callback-benadering. In Swift bereikte de adoptie van Swift Concurrency 68% onder gepubliceerde apps in 2025. iOS 16 voegde ondersteuning voor Swift Concurrency toe in UIKit, SwiftUI en Foundation, waardoor async/await de standaardtool werd voor alle nieuwe Apple-projecten.
Het belangrijkste verschil tussen async/await en threads is onderbreking zonder blokkering. Wanneer een functie await tegenkomt, wordt deze onderbroken, waardoor de huidige thread vrijkomt voor andere taken. Nadat de asynchrone bewerking is voltooid, wordt de functie hervat op dezelfde of een andere thread. Dit wordt cooperative multitasking genoemd.
Een async functie wordt gecompileerd tot een state machine — een finite state machine die de uitvoeringsstatus beheert. Op het await-punt slaat de compiler de context op (lokale variabelen, retouradres) en draagt de controle over aan de runtime. Wanneer het resultaat gereed is, herstelt de runtime de context en hervat de uitvoering.
In Swift is deze state machine geïmplementeerd op compilerniveau via SIL (Swift Intermediate Language). In Kotlin — via het Continuation Passing Style (CPS)-mechanisme: elke suspend-functie ontvangt een verborgen parameter Continuation, waarmee het resultaat wordt geretourneerd. Beide benaderingen garanderen veilige hervatting zonder geheugenlekken.
De callback-benadering vereist het doorgeven van een closure die wordt aangeroepen nadat de bewerking is voltooid. Bij drie opeenvolgende verzoeken ontstaat drie niveaus van nesting met foutafhandeling op elk niveau. Async/Await verandert dezelfde drie verzoeken in drie opeenvolgende regels met een enkel catch-blok.
GCD (DispatchQueue) lost het callback-hell-probleem op via seriële wachtrijen en DispatchGroup, maar blijft omslachtig. Voor een eenvoudige taak — user laden, dan friends, dan UI — zijn het maken van wachtrijen, groepen en notify-blokken nodig. Async/Await voert dezelfde taak uit met drie regels in één functie.
| Aspect | Callback | GCD | Async/Await |
|---|---|---|---|
| Nesting | Diep (pyramid of doom) | Gemiddeld (groepen+notify) | Lineair (flat) |
| Foutafhandeling | Per-callback | Handmatig | Uniform (try/catch) |
| Annulering | Handmatig | Beperkt | Task.cancel() |
| Thread safety | Handmatig | Via serial queue | Via MainActor |
Volgens Apple WWDC 2024 is Swift Concurrency async/await de aanbevolen benadering voor nieuwe code. GCD blijft voor integratie met C-bibliotheken en specifieke low-level scenario's voor threadverwerking.
Task is de basiseenheid van asynchroon werk in Swift. Een Task wordt gemaakt in de context van een bestaande actor of op een willekeurige thread. Binnen een Task kunnen async-functies worden aangeroepen via await. TaskGroup maakt het mogelijk meerdere onderliggende taken parallel uit te voeren en hun resultaten te verzamelen — een krachtigere versie van DispatchGroup.
Actor is een thread-safe reference type dat zijn status beschermt tegen race conditions. De compiler garandeert dat toegang tot eigenschappen van een actor alleen mogelijk is via async-aanroepen of binnen actor-isolatie. Actor vervangt seriële DispatchQueue voor het beschermen van gedeelde veranderlijke status zonder handmatige vergrendelingen.
Het principe van Structured Concurrency luidt: elke asynchrone taak heeft een ouder, en de ouder wordt niet voltooid voordat al zijn kinderen zijn voltooid. Als taak A Task { await B() } uitvoert, wacht A op B. Dit voorkomt taaklekkage en zorgt voor een voorspelbare levenscyclus.
In Kotlin is async/await geïmplementeerd via coroutines — lichtgewicht threads van de taal. Een functie wordt gemarkeerd met het trefwoord suspend (analoog aan Swift's async). Voor uitvoering wordt launch (fire-and-forget) of async (met resultaat) gebruikt. Await wordt aangeroepen om het resultaat van async te verkrijgen.
Dispatcher bepaalt op welke threadpool de coroutine wordt uitgevoerd: Dispatchers.Main voor UI, Dispatchers.IO voor netwerk/schijf en Dispatchers.Default voor CPU-intensieve taken. Structured Concurrency wordt gegarandeerd via CoroutineScope — bij annulering van de scope worden alle onderliggende coroutines geannuleerd.
Volgens JetBrains (2025) gebruikt 97% van de Android-apps in Google Play kotlinx.coroutines, en 82% daarvan gebruikt async/await voor netwerkverzoeken. Coroutines zijn de de facto standaard geworden voor asynchroon programmeren in Android. Kotlin Multiplatform ondersteunt ook coroutines, waardoor gedeelde asynchrone code mogelijk is op Android, iOS en de serverkant.
Laten we twee voorbeelden bekijken voor elke taal: sequentiële gegevenslading en parallelle verzoeken met TaskGroup of async/await.
await onderbreekt de uitvoering totdat gebruikersgegevens zijn verkregen, vervolgens totdat vrienden zijn verkregen. Geen geneste closures.
func loadProfile() async throws -> Profile {
let user = try await api.fetchUser()
let friends = try await api.fetchFriends(for: user.id)
return Profile(user: user, friends: friends)
}
TaskGroup voert taken parallel uit en verzamelt de resultaten in een array. De volgorde van resultaten kan afwijken van de startvolgorde.
func loadParallel() async throws -> [String] {
await withThrowingTaskGroup(of: String.self) { group in
group.addTask { try await api.fetchName() }
group.addTask { try await api.fetchEmail() }
group.addTask { try await api.fetchAvatar() }
var results = [String]()
for try await value in group { results.append(value) }
return results
}
}
suspend-functies kunnen andere suspend-functies aanroepen. Analoog aan Swift: sequentiële aanroepen.
suspend fun loadProfile(): Profile {
val user = api.fetchUser()
val friends = api.fetchFriends(user.id)
return Profile(user, friends)
}
In Kotlin wordt parallellisme bereikt via coroutineScope met async voor elk verzoek.
suspend fun loadParallel(): List<String> = coroutineScope {
val name = async { api.fetchName() }
val email = async { api.fetchEmail() }
val avatar = async { api.fetchAvatar() }
listOf(name.await(), email.await(), avatar.await())
}
Vergeten Task { } — een asynchrone functie kan niet worden aangeroepen vanuit een synchrone context zonder deze in een Task te verpakken. In Swift resulteert dit in een compilatiefout. In Kotlin is uitvoering via lifecycleScope of viewModelScope vereist. Het aanroepen van een suspend-functie vanuit een gewone functie wordt niet gecompileerd.
Niet elke functie hoeft asynchroon te worden gemaakt. CPU-intensieve taken zonder I/O hebben geen baat bij async — ze kunnen beter worden uitgevoerd op een DispatchQueue met .userInitiated QoS. Async/Await is geoptimaliseerd voor I/O-gebonden bewerkingen: netwerk, schijf, wachten.
Het gebruik van .result op de hoofdthread (synchroon wachten op het resultaat van een async-functie) leidt tot blokkering. In Swift wordt Task.synchronousWait niet aanbevolen door Apple. In Kotlin is runBlocking op de hoofdthread een antipatroon — gebruik lifecycleScope.
Als een Task niet wordt opgeslagen in een klasseeigenschap, wordt deze geannuleerd na het verlaten van de context. In Swift is Task scoped — de levenscyclus is gekoppeld aan de aanmaakcontext. Bewaar de Task in een eigenschap als een langdurige bewerking nodig is.
Veelgestelde vragen
Async/Await is een taalconstructie die geen wachtrijen vereist. DispatchQueue is een systeem-API voor threadbeheer. Async/await wordt gecompileerd naar een state machine op taalniveau, GCD naar een syscall op OS-niveau.
Nee — await onderbreekt de functie, maar geeft de thread vrij voor andere taken. Na voltooiing van de bewerking wordt de functie hervat op een beschikbare thread uit de pool.
Ja — via Task.cancel() in Swift of Job.cancel() in Kotlin. Annulering verspreidt zich naar alle onderliggende taken volgens Structured Concurrency. De code controleert de annuleringsvlag via Task.isCancelled of ensureActive().
MainActor is een actor waarvan eigenschappen en methoden altijd op de hoofdthread worden uitgevoerd. Door een functie te markeren met @MainActor garandeer je dat UI-updates op de juiste thread plaatsvinden, ter vervanging van DispatchQueue.main.async.
Async/Await is beschikbaar in Swift (iOS 13+), Kotlin (via kotlinx.coroutines), Dart (Flutter), JavaScript/TypeScript, Python, C#, Rust en Go (via goroutines met syntaxissuiker).
Samenvatting
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.
Lees ook