Suspend function — är en funktion med suspend-modifieraren som kan pausa sin exekvering utan att blockera tråden och återupptas senare i samma korutin. Enligt JetBrains Kotlin Docs, 2025 är suspend-funktioner den grundläggande byggstenen för korutiner och tillhandahåller asynkronitet utan callbacks. Varje suspend-funktion kompileras till en tillståndsmaskin baserad på Continuation, vilket möjliggör effektiv hantering av pauspunkter.
Huvudpunkter
Suspend function — är en funktion deklarerad med nyckelordet suspend som kan pausa exekvering i en eller flera punkter utan att blockera tråden. Varje anrop av en suspend-funktion inuti en annan suspend-funktion är en potentiell pauspunkt.
suspend fun fetchUserData(): User {
val response = httpClient.get("/user")
return parser.parse(response)
}
Kotlin-kompilatorn översätter en sådan funktion till en tillståndsmaskin. Varje pauspunkt (anrop av en annan suspend-funktion) blir ett tillstånd (label). Den aktuella tråden frigörs mellan tillstånden och efter slutförandet av den förväntade operationen fortsätter exekveringen från nästa tillstånd.
Suspend-funktioner dök upp i Kotlin 1.3 (2018) tillsammans med korutiner som en experimentell funktion och blev stabila i Kotlin 1.5 (2021). Före detta tillhandahölls asynkronitet i Kotlin/Java via callbacks, RxJava och CompletableFuture. Suspend-funktioner erbjöd ett alternativ med linjär syntax och automatisk trådhantering.
Att förstå den interna strukturen hos suspend-funktioner är nyckeln till att arbeta korrekt med korutiner. Till skillnad från vanliga funktioner kompileras varje suspend-funktion till en klass med Continuation-gränssnitt.
Kotlin-kompilatorn lägger till en parameter av typen Continuation i slutet av varje parameter i en suspend-funktion. Continuation innehåller:
Anta att vi har en suspend-funktion med två anrop till andra suspend-funktioner:
suspend fun process() {
val a = stepOne()
val b = stepTwo(a)
println(b)
}
Kompilatorn omvandlar den till en tillståndsmaskin med etiketter:
// Förenklad representation av genererad kod
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
}
}
}
Viktig observation: om funktionen returnerar COROUTINE_SUSPENDED frigörs den aktuella tråden. När den asynkrona operationen är slutförd anropas Continuation.resumeWith och tillståndsmaskinen fortsätter från nästa label.
Deklarationen av en suspend-funktion skiljer sig inte från en vanlig, förutom nyckelordet suspend före fun. Det finns bara en begränsning: en suspend-funktion kan endast anropas från en korutin eller en annan suspend-funktion.
suspend fun delayAndReturn(ms: Long): String {
delay(ms)
return "Done after ${ms}ms"
}
I detta exempel är delay också en suspend-funktion som pausar korutinen under ett angivet antal millisekunder utan att blockera tråden. Efter fördröjningen återupptas exekveringen.
fun main() = runBlocking {
val result = delayAndReturn(1000)
println(result)
}
runBlocking skapar en bro mellan den vanliga världen och korutiner. Inuti lambdan är anrop av valfri suspend-funktion tillåtet.
Kotlin stöder suspend-versioner av funktionella typer — suspend () -> T och suspend (A) -> B. Detta gör det möjligt att skicka asynkrona lambdor till högre ordningens funktioner:
suspend fun withRetry (
retries: Int = 3,
block: suspend () -> T
): T {
repeat(retries - 1) {
try { return block() }
catch (_: Exception) { delay(100) }
}
return block()
}
Funktionen withRetry accepterar en suspend-lambda och upprepar dess exekvering vid fel. Detta är ett typiskt mönster för nätverksförfrågningar med återförsök.
Skillnaderna mellan suspend och vanliga funktioner går utöver att bara lägga till en modifierare. Låt oss undersöka de grundläggande skillnaderna.
| Egenskap | Vanlig funktion | Suspend-funktion |
|---|---|---|
| Exekveringstråd | Blockerar tråden tills klar | Kan frigöra tråden och återuppta senare |
| Kompilatorparametrar | Endast angivna parametrar | Implicit Continuation i slutet |
| Anrop från vanlig funktion | Ja | Nej |
| Stack | Fysisk trådstack | Tillståndsmaskin på heap + fysisk stack mellan punkter |
| Retur | Direkt värde | Värde eller COROUTINE_SUSPENDED |
| Prestanda | Minimal overhead | ~några nanosekunder för tillståndsmaskin (Kotlin 1.9+) |
En vanlig funktion har ingen Continuation — ingen plats att spara tillstånd och återuppta exekvering. Om du behöver anropa en suspend-funktion från en vanlig funktion, använd runBlocking (för tester) eller CoroutineScope.launch (för produktion med hänsyn till livscykeln).
Låt oss undersöka tre verkliga scenarier för användning av suspend-funktioner i Android-appar med Kotlin.
Room stöder suspend-funktioner direkt — frågan körs automatiskt i bakgrunden:
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :id")
suspend fun getUser(id: Int): User?
@Insert
suspend fun insertUser(user: User)
}
Room använder internt Dispatchers.IO för att utföra frågan och resultatet returneras till den dispatcher där suspend-funktionen anropades.
class ProfileViewModel : ViewModel() {
private val repo = ProfileRepository()
fun loadProfile(id: String) {
viewModelScope.launch {
val profile = repo.getProfile(id)
_profile.value = profile
}
}
}
ViewModelScope.launch skapar en korutin inom vilken suspend-funktionen getProfile anropas. Efter att resultatet erhållits uppdateras UI:t på huvudtråden.
suspend fun placeOrder(cart: Cart): OrderResult {
val validated = validateCart(cart)
val payment = processPayment(validated)
val receipt = sendReceipt(payment)
return receipt
}
Tre suspend-funktioner körs sekventiellt. Vid varje steg kan korutinen stanna utan att blockera tråden. Om ett steg kastar ett undantag — körs inte de andra, vilket skyddar mot ogiltiga beställningstillstånd.
Även erfarna Kotlin-utvecklare gör misstag vid design av suspend-funktioner. Låt oss titta på de vanligaste.
En suspend-funktion gör inte koden automatiskt asynkron. Thread.sleep(), InputStream.read() och andra blockerande anrop kommer fortfarande att blockera tråden. Använd withContext(Dispatchers.IO) för att omsluta blockerande operationer.
Om en funktion inte anropar andra suspend-funktioner och inte utför asynkrona operationer — är suspend-modifieraren överflödig. Den lägger till overhead för tillståndsmaskinen och begränsar anropskontexten. Gör en funktion suspend endast när den verkligen pausar.
Vid avbrytning av korutin kastar suspend-funktioner CancellationException. Fånga den inte tanklöst — du berövar den anropande koden möjligheten att korrekt slutföra avbrytningen. Om du behöver utföra en slutgiltig operation, använd ett finally-block och NonCancellable.
suspend fun safeOperation() {
try {
doWork()
} finally {
withContext(NonCancellable) {
cleanup()
}
}
}
finally-blocket körs alltid, inklusive vid avbrytning, och NonCancellable garanterar att rensningen inte avbryts.
En suspend-funktion kan inte anropas direkt från en callback utan att skapa en korutin. Använd suspendCoroutine eller suspendCancellableCoroutine för att anpassa callback-stil till korutiner.
Vanliga frågor
Ja, tekniskt sett kan en suspend-funktion inte anropa andra suspend-funktioner. Kompilatorn skapar en tillståndsmaskin med ett tillstånd (label 0). En sådan funktion har dock ingen praktisk nytta — den körs som en vanlig, men med overhead. Använd inte suspend i onödan.
Kotlin tillhandahåller kotlinx-coroutines-debug — ett bibliotek med DebugProbes och verktyg för korutinspårning. I Android Studio från och med version Arctic Fox finns en inbyggd flik Coroutines i Debugger som visar aktiva korutiner, deras tillstånd och pauspunkter.
Varje pauspunkt skapar ett nytt tillstånd i tillståndsmaskinen. För de flesta applikationer är overhead för en punkt några nanosekunder (Kotlin 1.9+). Endast när det finns tiotusentals punkter i en loop är det värt att kombinera operationer eller använda sequence/flow.
I Kotlin är suspend en modifierare av funktionstypen, inte en markör för returvärdet (som async i C#). Varje suspend-funktion kan ha godtyckliga parametrar och returtyp, och dess anrop skiljer sig syntaktiskt inte från ett vanligt funktionsanrop — det finns ingen await-operator vid anropsplatsen.
Använd suspendCancellableCoroutine för anpassning. Inuti skickar du callback-registreringen som anropar continuation.resume() och returnerar en avbrytningstoken om callbacken stöder avprenumeration. Detta är ett standardmönster för att omsluta gamla Android-API:er.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också