suspend în Kotlin

suspend marchează o funcție care se poate opri (suspenda) fără să blocheze firul și poate continua mai târziu; se apelează din corutine.

Sintaxă
suspend fun incarca(): Date { ... }

val bloc: suspend () -> Int = { ... }
bloc.startCoroutine(Continuation(EmptyCoroutineContext) { rezultat -> ... })

Ce face suspend

O funcție suspend poate să-și întrerupă execuția la un punct (de exemplu cât așteaptă un răspuns din rețea) și să continue mai târziu de unde a rămas, fără să țină blocat firul de execuție. O funcție suspend poate fi apelată doar din altă funcție suspend sau dintr-o corutină. Din main obișnuit, apelul direct nu compilează.

Compilatorul transformă fiecare funcție suspend într-o funcție care primește un parametru ascuns, Continuation: „ce trebuie făcut după”. Biblioteca standard oferă doar piesele de bază: startCoroutine, suspendCoroutine, Continuation, sequence { }. În practică se folosește biblioteca separată kotlinx.coroutines, cu runBlocking, launch, async și delay.

Exemplele 1 și 2 folosesc doar biblioteca standard și rulează aici. Exemplul 3 folosește kotlinx.coroutines, care nu este instalată pe serverul care verifică exemplele; rezultatul lui este scris de mână.

Exemple

Exemplul 1: Pornirea unei funcții suspend cu biblioteca standard

startCoroutine pornește lambda suspend. Când se termină, se apelează Continuation cu rezultatul. Aici nicio funcție nu se suspendă cu adevărat, deci totul rulează imediat.

import kotlin.coroutines.Continuation
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.startCoroutine

suspend fun pretProdus(produs: String): Int = when (produs) {
    "caiet" -> 7
    "pix" -> 3
    else -> 10
}

suspend fun totalCos(): Int = pretProdus("caiet") * 2 + pretProdus("pix")

fun main() {
    val bloc: suspend () -> Int = { totalCos() }
    bloc.startCoroutine(Continuation(EmptyCoroutineContext) { rezultat ->
        println("Total coș: ${rezultat.getOrThrow()} lei")
    })
    println("main s-a terminat")
}

Rezultat

Total coș: 17 lei
main s-a terminat

Exemplul 2: Suspendare reală cu suspendCoroutine

asteaptaMesaj se suspendă și păstrează continuarea. main continuă între timp, iar resume trezește corutina exact de unde a rămas.

import kotlin.coroutines.Continuation
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.resume
import kotlin.coroutines.startCoroutine
import kotlin.coroutines.suspendCoroutine

var inAsteptare: Continuation<String>? = null

suspend fun asteaptaMesaj(): String = suspendCoroutine { cont -> inAsteptare = cont }

fun main() {
    val bloc: suspend () -> Unit = {
        println("1. Corutina pornește și așteaptă un mesaj")
        val mesaj = asteaptaMesaj()
        println("3. Corutina a primit: $mesaj")
    }
    bloc.startCoroutine(Continuation(EmptyCoroutineContext) { println("4. Corutina s-a terminat") })

    println("2. main lucrează cât timp corutina e suspendată")
    inAsteptare?.resume("Comanda #1024 a fost livrată în Iași")
}

Rezultat

1. Corutina pornește și așteaptă un mesaj
2. main lucrează cât timp corutina e suspendată
3. Corutina a primit: Comanda #1024 a fost livrată în Iași
4. Corutina s-a terminat

Exemplul 3: Cu biblioteca kotlinx.coroutines (nu rulează aici)

Cu dependența org.jetbrains.kotlinx:kotlinx-coroutines-core: delay este o funcție suspend care așteaptă fără să blocheze firul, iar async pornește cele două descărcări în paralel. Totalul durează cât cea mai lungă, nu cât suma lor.

import kotlinx.coroutines.*

suspend fun descarca(fisier: String, ms: Long): String {
    delay(ms)
    return "$fisier ($ms ms)"
}

fun main() = runBlocking {
    val start = System.currentTimeMillis()
    val a = async { descarca("facturi.pdf", 300) }
    val b = async { descarca("poze.zip", 500) }
    println("Gata: ${a.await()}, ${b.await()}")
    println("Durată totală: ~${(System.currentTimeMillis() - start) / 100 * 100} ms")
}

Rezultat (cu kotlinx.coroutines instalată)

Gata: facturi.pdf (300 ms), poze.zip (500 ms)
Durată totală: ~500 ms

Greșeli frecvente

  • Apelarea unei funcții suspend din main obișnuit: eroare suspend function 'suspend fun x(): Int' can only be called from a coroutine or another suspend function. Fie faci suspend fun main(), fie pornești o corutină.
  • Credința că suspend face codul să ruleze pe alt fir: cuvântul doar permite suspendarea. Pe ce fir rulează codul decide cine pornește corutina (de exemplu Dispatchers.IO în kotlinx.coroutines).
  • Folosirea lui Thread.sleep într-o funcție suspend: blochează firul. Cu kotlinx.coroutines folosește delay.

Comenzi înrudite

  • Corutine (ideea): O corutină este un calcul care se poate opri și relua; Kotlin o folosește pentru generatoare (sequence) și pentru cod asincron simplu.
  • sequence: Construiește o secvență dintr-un bloc de cod care dă valori una câte una cu yield; codul se oprește și continuă la cerere.
  • lambda: O lambda este o funcție fără nume, scrisă între acolade, pe care o poți pune într-o variabilă sau trimite altei funcții.
  • inline, noinline și crossinline: inline copiază corpul funcției și lambda-urile ei în locul apelului; noinline și crossinline controlează lambda-urile primite.