Suspend function — este o funcție cu modificatorul suspend care își poate suspenda execuția fără a bloca firul de execuție și poate reveni mai târziu în aceeași corutină. Conform JetBrains Kotlin Docs, 2025, funcțiile suspend sunt elementul fundamental al corutinelor, asigurând asincronismul fără callback-uri. Fiecare funcție suspend este compilată într-o mașină de stări bazată pe Continuation, ceea ce permite gestionarea eficientă a punctelor de suspendare.
Principalele puncte
Suspend function — este o funcție declarată cu cuvântul cheie suspend care poate suspenda execuția într-unul sau mai multe puncte fără a bloca firul de execuție. Fiecare apel al unei funcții suspend în interiorul altei funcții suspend este un potențial punct de suspendare.
suspend fun fetchUserData(): User {
val response = httpClient.get("/user")
return parser.parse(response)
}
Compilatorul Kotlin transformă o astfel de funcție într-o mașină de stări. Fiecare punct de suspendare (apelul altei funcții suspend) devine o stare (label). Firul curent este eliberat între stări, iar după finalizarea operației așteptate, execuția se reia de la starea următoare.
Funcțiile suspend au apărut în Kotlin 1.3 (2018) odată cu corutinele ca funcționalitate experimentală și au devenit stabile în Kotlin 1.5 (2021). Înainte de aceasta, asincronismul în Kotlin/Java era asigurat prin callback-uri, RxJava și CompletableFuture. Funcțiile suspend au oferit o alternativă cu sintaxă liniară și gestionare automată a firelor de execuție.
Înțelegerea structurii interne a funcțiilor suspend este cheia pentru a lucra corect cu corutinele. Spre deosebire de funcțiile obișnuite, fiecare funcție suspend este compilată într-o clasă cu interfața Continuation.
Compilatorul Kotlin adaugă un parametru de tip Continuation la sfârșitul fiecărui parametru al funcției suspend. Continuation conține:
Să presupunem că avem o funcție suspend cu două apeluri către alte funcții suspend:
suspend fun process() {
val a = stepOne()
val b = stepTwo(a)
println(b)
}
Compilatorul o transformă într-o mașină de stări cu etichete:
// Reprezentare simplificată a codului generat
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
}
}
}
Observația cheie: dacă funcția returnează COROUTINE_SUSPENDED, firul curent este eliberat. Când operația asincronă se finalizează, Continuation.resumeWith este apelată, iar mașina de stări continuă de la următorul label.
Declarația unei funcții suspend nu diferă de cea obișnuită, cu excepția cuvântului cheie suspend înainte de fun. Există o singură restricție: o funcție suspend poate fi apelată doar dintr-o corutină sau dintr-o altă funcție suspend.
suspend fun delayAndReturn(ms: Long): String {
delay(ms)
return "Done after ${ms}ms"
}
În acest exemplu, delay este de asemenea o funcție suspend care oprește corutina pentru un număr specificat de milisecunde fără a bloca firul. După întârziere, execuția este reluată.
fun main() = runBlocking {
val result = delayAndReturn(1000)
println(result)
}
runBlocking creează o punte între lumea obișnuită și corutine. În interiorul lambda, este permis apelarea oricăror funcții suspend.
Kotlin suportă versiunile suspend ale tipurilor funcționale — suspend () -> T și suspend (A) -> B. Aceasta permite transmiterea lambda-urilor asincrone către funcții de ordin superior:
suspend fun withRetry (
retries: Int = 3,
block: suspend () -> T
): T {
repeat(retries - 1) {
try { return block() }
catch (_: Exception) { delay(100) }
}
return block()
}
Funcția withRetry primește un lambda suspend și repetă execuția acestuia la erori. Este un model tipic pentru cererile de rețea cu reîncercări.
Diferențele dintre funcțiile suspend și cele obișnuite depășesc simpla adăugare a unui modificator. Să examinăm diferențele esențiale.
| Caracteristică | Funcție obișnuită | Funcție suspend |
|---|---|---|
| Fir de execuție | Blochează firul până la finalizare | Poate elibera firul și relua mai târziu |
| Parametri compilator | Doar parametrii specificați | Continuation implicit la sfârșit |
| Apel din funcție obișnuită | Da | Nu |
| Stivă | Stiva fizică a firului | Mașină de stări în heap + stivă fizică între puncte |
| Returnare | Valoare directă | Valoare sau COROUTINE_SUSPENDED |
| Performanță | Costuri minime | ~câteva nanosecunde pentru mașina de stări (Kotlin 1.9+) |
O funcție obișnuită nu are Continuation — nu are unde să salveze starea și cu ce să reia execuția. Dacă trebuie să apelați o funcție suspend dintr-o funcție obișnuită, utilizați runBlocking (pentru teste) sau CoroutineScope.launch (pentru producție, ținând cont de ciclul de viață).
Să examinăm trei scenarii reale de utilizare a funcțiilor suspend în aplicații Android cu Kotlin.
Room suportă funcțiile suspend direct — interogarea se execută automat în fundal:
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :id")
suspend fun getUser(id: Int): User?
@Insert
suspend fun insertUser(user: User)
}
Room utilizează intern Dispatchers.IO pentru executarea interogării, iar rezultatul este returnat dispecerului în care a fost apelată funcția suspend.
class ProfileViewModel : ViewModel() {
private val repo = ProfileRepository()
fun loadProfile(id: String) {
viewModelScope.launch {
val profile = repo.getProfile(id)
_profile.value = profile
}
}
}
ViewModelScope.launch creează o corutină în interiorul căreia este apelată funcția suspend getProfile. După obținerea rezultatului, UI este actualizat pe firul principal.
suspend fun placeOrder(cart: Cart): OrderResult {
val validated = validateCart(cart)
val payment = processPayment(validated)
val receipt = sendReceipt(payment)
return receipt
}
Trei funcții suspend se execută secvențial. La fiecare pas, corutina se poate opri fără a bloca firul. Dacă un pas generează o excepție — ceilalți nu se execută, ceea ce protejează împotriva stărilor incorecte ale comenzii.
Chiar și dezvoltatorii experimentați Kotlin fac greșeli la proiectarea funcțiilor suspend. Să analizăm cele mai frecvente.
O funcție suspend nu face codul automat asincron. Thread.sleep(), InputStream.read() și alte apeluri blocante vor bloca totuși firul. Utilizați withContext(Dispatchers.IO) pentru a încapsula operațiile blocante.
Dacă o funcție nu apelează alte funcții suspend și nu execută operații asincrone — modificatorul suspend este redundant. Adaugă costuri suplimentare pentru mașina de stări și limitează contextul de apel. Faceți o funcție suspend doar atunci când se suspendă cu adevărat.
La anularea corutinei, funcțiile suspend aruncă CancellationException. Nu-l prindeți fără discernământ — lipsiți codul apelant de posibilitatea de a finaliza corect anularea. Dacă trebuie să executați o operație de finalizare, utilizați blocul finally și NonCancellable.
suspend fun safeOperation() {
try {
doWork()
} finally {
withContext(NonCancellable) {
cleanup()
}
}
}
Blocul finally se execută întotdeauna, inclusiv la anulare, iar NonCancellable garantează că cleanup-ul nu va fi întrerupt.
Nu se poate apela o funcție suspend direct dintr-un callback fără a crea o corutină. Utilizați suspendCoroutine sau suspendCancellableCoroutine pentru a adapta stilul callback la corutine.
Întrebări frecvente
Da, tehnic o funcție suspend poate să nu apeleze alte funcții suspend. Compilatorul va crea o mașină de stări cu o singură stare (label 0). Totuși, o astfel de funcție nu are utilitate practică — se execută ca una obișnuită, dar cu costuri suplimentare. Nu utilizați suspend fără necesitate.
Kotlin oferă kotlinx-coroutines-debug — o bibliotecă cu DebugProbes și instrumente de trasare a corutinelor. În Android Studio începând cu versiunea Arctic Fox există o filă încorporată Coroutines în Debugger care arată corutinele active, starea și punctele lor de suspendare.
Fiecare punct de suspendare creează o stare nouă în mașina de stări. Pentru majoritatea aplicațiilor, costul unui punct este de unități de nanosecunde (Kotlin 1.9+). Doar la zeci de mii de puncte într-o buclă merită să combinați operațiile sau să utilizați sequence/flow.
În Kotlin, suspend este un modificator al tipului de funcție, nu un marcator al valorii returnate (ca async în C#). Orice funcție suspend poate avea parametri și tip de returnare arbitrare, iar apelul său sintactic nu diferă de apelul unei funcții obișnuite — nu există operatorul await la locul apelului.
Utilizați suspendCancellableCoroutine pentru adaptare. În interior, transmiteți înregistrarea callback-ului care apelează continuation.resume() și returnați un token de anulare dacă callback-ul suportă dezabonarea. Acesta este modelul standard pentru încapsularea API-urilor vechi Android.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și