Suspend function — is een functie met de suspend-modifier die de uitvoering kan onderbreken zonder de thread te blokkeren en later kan hervatten in dezelfde coroutine. Volgens JetBrains Kotlin Docs, 2025 zijn suspend-functies de fundamentele bouwstenen van coroutines en bieden ze asynchroniteit zonder callbacks. Elke suspend-functie wordt gecompileerd naar een op Continuation gebaseerde toestandsmachine, waardoor suspensiepunten efficiënt kunnen worden beheerd.
Belangrijkste punten
Suspend function — is een functie gedeclareerd met het sleutelwoord suspend die de uitvoering kan onderbreken op een of meer punten zonder de thread te blokkeren. Elke aanroep van een suspend-functie binnen een andere suspend-functie is een potentieel suspensiepunt.
suspend fun fetchUserData(): User {
val response = httpClient.get("/user")
return parser.parse(response)
}
De Kotlin-compiler vertaalt zo'n functie naar een toestandsmachine. Elk suspensiepunt (aanroep van een andere suspend-functie) wordt een toestand (label). De huidige thread wordt vrijgegeven tussen toestanden en na voltooiing van de verwachte bewerking wordt de uitvoering hervat vanuit de volgende toestand.
Suspend-functies verschenen in Kotlin 1.3 (2018) samen met coroutines als een experimentele functie en werden stabiel in Kotlin 1.5 (2021). Daarvoor werd asynchroniteit in Kotlin/Java verzorgd door callbacks, RxJava en CompletableFuture. Suspend-functies boden een alternatief met lineaire syntax en automatisch threadbeheer.
Inzicht in de interne structuur van suspend-functies is de sleutel tot correct werken met coroutines. In tegenstelling tot gewone functies wordt elke suspend-functie gecompileerd naar een klasse met de Continuation-interface.
De Kotlin-compiler voegt een parameter van het type Continuation toe aan het einde van elke parameter van een suspend-functie. Continuation bevat:
Stel dat we een suspend-functie hebben met twee aanroepen naar andere suspend-functies:
suspend fun process() {
val a = stepOne()
val b = stepTwo(a)
println(b)
}
De compiler zet dit om in een toestandsmachine met labels:
// Vereenvoudigde weergave van gegenereerde code
fun process(cont: Continuation<Unit>): Any? {
val cont = cont as ProcessContinuation
when (cont.label) {
0 -> {
cont.label = 1
if (stepOne(cont) == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
}
1 -> {
cont.label = 2
val a = cont.result as TypeA
if (stepTwo(a, cont) == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
}
2 -> {
println(cont.result)
Unit
}
}
}
Belangrijke observatie: als de functie COROUTINE_SUSPENDED retourneert, wordt de huidige thread vrijgegeven. Wanneer de asynchrone bewerking is voltooid, wordt Continuation.resumeWith aangeroepen en gaat de toestandsmachine verder vanaf het volgende label.
De declaratie van een suspend-functie verschilt niet van een gewone, behalve het sleutelwoord suspend voor fun. Er is slechts één beperking: een suspend-functie kan alleen worden aangeroepen vanuit een coroutine of een andere suspend-functie.
suspend fun delayAndReturn(ms: Long): String {
delay(ms)
return "Done after ${ms}ms"
}
In dit voorbeeld is delay ook een suspend-functie die de coroutine onderbreekt voor een opgegeven aantal milliseconden zonder de thread te blokkeren. Na de vertraging wordt de uitvoering hervat.
fun main() = runBlocking {
val result = delayAndReturn(1000)
println(result)
}
runBlocking creëert een brug tussen de gewone wereld en coroutines. Binnen de lambda is het aanroepen van elke suspend-functie toegestaan.
Kotlin ondersteunt suspend-versies van functionele typen — suspend () -> T en suspend (A) -> B. Dit maakt het mogelijk asynchrone lambdas door te geven aan functies van hogere orde:
suspend fun withRetry (
retries: Int = 3,
block: suspend () -> T
): T {
repeat(retries - 1) {
try { return block() }
catch (_: Exception) { delay(100) }
}
return block()
}
De functie withRetry accepteert een suspend-lambda en herhaalt de uitvoering bij fouten. Dit is een typisch patroon voor netwerkverzoeken met herpogingen.
De verschillen tussen suspend en gewone functies gaan verder dan het eenvoudig toevoegen van een modifier. Laten we de fundamentele verschillen bekijken.
| Kenmerk | Gewone functie | Suspend-functie |
|---|---|---|
| Uitvoeringsthread | Blokkeert de thread tot voltooiing | Kan thread vrijgeven en later hervatten |
| Compilerparameters | Alleen opgegeven parameters | Impliciete Continuation aan het einde |
| Aanroep vanuit gewone functie | Ja | Nee |
| Stack | Fysieke thread-stack | Toestandsmachine op heap + fysieke stack tussen punten |
| Return | Directe waarde | Waarde of COROUTINE_SUSPENDED |
| Prestaties | Minimale overhead | ~enkele nanoseconden voor toestandsmachine (Kotlin 1.9+) |
Een gewone functie heeft geen Continuation — geen plek om de status op te slaan en de uitvoering te hervatten. Als u een suspend-functie vanuit een gewone functie moet aanroepen, gebruik dan runBlocking (voor tests) of CoroutineScope.launch (voor productie met inachtneming van de levenscyclus).
Laten we drie realistische scenario's bekijken voor het gebruik van suspend-functies in Android-apps met Kotlin.
Room ondersteunt suspend-functies direct — de query wordt automatisch op de achtergrond uitgevoerd:
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :id")
suspend fun getUser(id: Int): User?
@Insert
suspend fun insertUser(user: User)
}
Room gebruikt intern Dispatchers.IO voor het uitvoeren van de query en het resultaat wordt teruggegeven aan de dispatcher waarin de suspend-functie werd aangeroepen.
class ProfileViewModel : ViewModel() {
private val repo = ProfileRepository()
fun loadProfile(id: String) {
viewModelScope.launch {
val profile = repo.getProfile(id)
_profile.value = profile
}
}
}
ViewModelScope.launch creëert een coroutine waarbinnen de suspend-functie getProfile wordt aangeroepen. Nadat het resultaat is verkregen, wordt de UI bijgewerkt op de hoofdthread.
suspend fun placeOrder(cart: Cart): OrderResult {
val validated = validateCart(cart)
val payment = processPayment(validated)
val receipt = sendReceipt(payment)
return receipt
}
Drie suspend-functies worden opeenvolgend uitgevoerd. Bij elke stap kan de coroutine stoppen zonder de thread te blokkeren. Als een stap een uitzondering genereert — worden de andere niet uitgevoerd, wat beschermt tegen onjuiste bestelstatussen.
Zelfs ervaren Kotlin-ontwikkelaars maken fouten bij het ontwerpen van suspend-functies. Laten we de meest voorkomende bekijken.
Een suspend-functie maakt code niet automatisch asynchroon. Thread.sleep(), InputStream.read() en andere blokkerende aanroepen blokkeren nog steeds de thread. Gebruik withContext(Dispatchers.IO) om blokkerende bewerkingen in te pakken.
Als een functie geen andere suspend-functies aanroept en geen asynchrone bewerkingen uitvoert — is de suspend-modifier overbodig. Het voegt overhead toe aan de toestandsmachine en beperkt de aanroepcontext. Maak een functie alleen suspend als deze daadwerkelijk onderbroken wordt.
Bij het annuleren van een coroutine gooien suspend-functies een CancellationException. Vang deze niet gedachteloos — u ontneemt de aanroepende code de mogelijkheid om de annulering correct af te ronden. Als u een finaliserende bewerking moet uitvoeren, gebruik dan een finally-blok en NonCancellable.
suspend fun safeOperation() {
try {
doWork()
} finally {
withContext(NonCancellable) {
cleanup()
}
}
}
Het finally-blok wordt altijd uitgevoerd, inclusief bij annulering, en NonCancellable garandeert dat de opschoning niet wordt onderbroken.
Een suspend-functie kan niet direct vanuit een callback worden aangeroepen zonder een coroutine te maken. Gebruik suspendCoroutine of suspendCancellableCoroutine om de callback-stijl aan te passen aan coroutines.
Veelgestelde vragen
Ja, technisch gezien kan een suspend-functie andere suspend-functies niet aanroepen. De compiler maakt een toestandsmachine met één toestand (label 0). Zo'n functie heeft echter geen praktisch nut — hij wordt uitgevoerd als een gewone, maar met overhead. Gebruik suspend niet onnodig.
Kotlin biedt kotlinx-coroutines-debug — een bibliotheek met DebugProbes en hulpmiddelen voor het traceren van coroutines. In Android Studio vanaf versie Arctic Fox is er een ingebouwd Coroutines-tabblad in de Debugger dat actieve coroutines, hun status en suspensiepunten toont.
Elk suspensiepunt creëert een nieuwe toestand in de toestandsmachine. Voor de meeste toepassingen bedraagt de overhead van één punt enkele nanoseconden (Kotlin 1.9+). Alleen bij tienduizenden punten in een lus is het de moeite waard om bewerkingen te combineren of sequence/flow te gebruiken.
In Kotlin is suspend een modifier van het functietype, geen markering van de retourwaarde (zoals async in C#). Elke suspend-functie kan willekeurige parameters en retourtypen hebben en de aanroep ervan verschilt syntactisch niet van een gewone functieaanroep — er is geen await-operator op de aanroepplaats.
Gebruik suspendCancellableCoroutine voor aanpassing. Binnenin geeft u de callback-registratie door die continuation.resume() aanroept en retourneert u een annuleringstoken als de callback uitschrijven ondersteunt. Dit is een standaardpatroon voor het inpakken van oude Android-API's.
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