Suspend function — egy függvény a suspend módosítóval, amely felfüggesztheti a végrehajtását a szál blokkolása nélkül, és később ugyanabban a korutinban folytathatja. A JetBrains Kotlin Docs, 2025 szerint a suspend függvények a korutinok alapvető építőkövei, aszinkronitást biztosítva callback-ek nélkül. Minden suspend függvény egy Continuation-alapú állapotgépre fordul, ami lehetővé teszi a felfüggesztési pontok hatékony kezelését.
Főbb pontok
Suspend function — egy a suspend kulcsszóval deklarált függvény, amely egy vagy több ponton felfüggesztheti a végrehajtást a szál blokkolása nélkül. Minden suspend függvény hívása egy másik suspend függvényen belül potenciális felfüggesztési pont.
suspend fun fetchUserData(): User {
val response = httpClient.get("/user")
return parser.parse(response)
}
A Kotlin fordító az ilyen függvényt állapotgépre fordítja. Minden felfüggesztési pont (másik suspend függvény hívása) egy állapottá (label) válik. Az aktuális szál felszabadul az állapotok között, és a várt művelet befejezése után a végrehajtás a következő állapottól folytatódik.
A suspend függvények a Kotlin 1.3-ban (2018) jelentek meg a korutinokkal együtt kísérleti funkcióként, és a Kotlin 1.5-ben (2021) váltak stabillá. Ezelőtt az aszinkronitást Kotlin/Java-ban callback-ek, RxJava és CompletableFuture biztosította. A suspend függvények alternatívát kínáltak lineáris szintaxissal és automatikus szálkezeléssel.
A suspend függvények belső szerkezetének megértése a kulcs a korutinokkal való helyes munkához. A hétköznapi függvényektől eltérően minden suspend függvény osztályra Continuation interfésszel fordul.
A Kotlin fordító egy Continuation típusú paramétert ad hozzá a suspend függvény minden paraméterének végéhez. A Continuation tartalmazza:
Tegyük fel, hogy van egy suspend függvényünk két másik suspend függvény hívásával:
suspend fun process() {
val a = stepOne()
val b = stepTwo(a)
println(b)
}
A fordító állapotgéppé alakítja címkékkel:
// Generált kód egyszerűsített reprezentációja
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
}
}
}
Kulcsfontosságú megfigyelés: ha a függvény COROUTINE_SUSPENDED értéket ad vissza, az aktuális szál felszabadul. Amikor az aszinkron művelet befejeződik, a Continuation.resumeWith meghívásra kerül, és az állapotgép a következő label-től folytatja.
A suspend függvény deklarálása nem különbözik a hétköznapitól, kivéve a suspend kulcsszót a fun előtt. Csak egy korlátozás van: suspend függvény csak korutinból vagy másik suspend függvényből hívható.
suspend fun delayAndReturn(ms: Long): String {
delay(ms)
return "Done after ${ms}ms"
}
Ebben a példában a delay is egy suspend függvény, amely felfüggeszti a korutint a megadott ezredmásodpercre anélkül, hogy blokkolná a szálat. A késleltetés után a végrehajtás folytatódik.
fun main() = runBlocking {
val result = delayAndReturn(1000)
println(result)
}
A runBlocking hidat képez a hétköznapi világ és a korutinok között. A lambdán belül bármely suspend függvény hívása engedélyezett.
A Kotlin támogatja a funkcionális típusok suspend verzióit — suspend () -> T és suspend (A) -> B. Ez lehetővé teszi aszinkron lambdák átadását magasabb rendű függvényeknek:
suspend fun withRetry (
retries: Int = 3,
block: suspend () -> T
): T {
repeat(retries - 1) {
try { return block() }
catch (_: Exception) { delay(100) }
}
return block()
}
A withRetry függvény egy suspend lambdát fogad el, és hiba esetén megismétli a végrehajtását. Ez tipikus minta hálózati kérésekhez újrapróbálkozásokkal.
A suspend és hétköznapi függvények közötti különbségek túlmutatnak a módosító egyszerű hozzáadásán. Vizsgáljuk meg az alapvető különbségeket.
| Jellemző | Hétköznapi függvény | Suspend függvény |
|---|---|---|
| Végrehajtási szál | Blokkolja a szálat a befejezésig | Felszabadíthatja a szálat és később folytathatja |
| Fordító paraméterei | Csak a megadott paraméterek | Implicit Continuation a végén |
| Hívás hétköznapi függvényből | Igen | Nem |
| Verem | A szál fizikai verme | Állapotgép a heap-en + fizikai verem a pontok között |
| Visszatérés | Közvetlen érték | Érték vagy COROUTINE_SUSPENDED |
| Teljesítmény | Minimális többletköltség | ~néhány nanoszekundum az állapotgépért (Kotlin 1.9+) |
A hétköznapi függvénynek nincs Continuationja — nincs hova menteni az állapotot és mivel folytatni a végrehajtást. Ha egy suspend függvényt kell hívnia egy hétköznapi függvényből, használja a runBlocking-ot (tesztekhez) vagy a CoroutineScope.launch-ot (éles használatra, figyelembe véve az életciklust).
Vizsgáljuk meg a suspend függvények használatának három valós forgatókönyvét Android alkalmazásokban Kotlinnal.
A Room közvetlenül támogatja a suspend függvényeket — a lekérdezés automatikusan a háttérben fut:
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :id")
suspend fun getUser(id: Int): User?
@Insert
suspend fun insertUser(user: User)
}
A Room belsőleg a Dispatchers.IO-t használja a lekérdezés végrehajtásához, és az eredmény abba a diszpécserbe kerül vissza, ahol a suspend függvényt hívták.
class ProfileViewModel : ViewModel() {
private val repo = ProfileRepository()
fun loadProfile(id: String) {
viewModelScope.launch {
val profile = repo.getProfile(id)
_profile.value = profile
}
}
}
A ViewModelScope.launch létrehoz egy korutint, amelyen belül a getProfile suspend függvény meghívásra kerül. Az eredmény megszerzése után a UI a fő szálon frissül.
suspend fun placeOrder(cart: Cart): OrderResult {
val validated = validateCart(cart)
val payment = processPayment(validated)
val receipt = sendReceipt(payment)
return receipt
}
Három suspend függvény szekvenciálisan hajtódik végre. Minden lépésnél a korutin megállhat a szál blokkolása nélkül. Ha egy lépés kivételt dob — a többiek nem hajtódnak végre, ami véd a rendelés helytelen állapotaitól.
Még tapasztalt Kotlin fejlesztők is hibáznak a suspend függvények tervezésekor. Nézzük a leggyakoribbakat.
A suspend függvény nem teszi a kódot automatikusan aszinkronná. A Thread.sleep(), InputStream.read() és más blokkoló hívások továbbra is blokkolják a szálat. Használja a withContext(Dispatchers.IO)-t a blokkoló műveletek becsomagolásához.
Ha egy függvény nem hív más suspend függvényeket és nem végez aszinkron műveleteket — a suspend módosító felesleges. Többletköltséget ad az állapotgéphez és korlátozza a hívási kontextust. Csak akkor tegye a függvényt suspend-é, ha az ténylegesen felfüggeszt.
A korutin megszakításakor a suspend függvények CancellationException-t dobnak. Ne fogja be gondolkodás nélkül — megfosztja a hívó kódot a megszakítás helyes befejezésének lehetőségétől. Ha véglegesítő műveletet kell végrehajtania, használja a finally blokkot és a NonCancellable-t.
suspend fun safeOperation() {
try {
doWork()
} finally {
withContext(NonCancellable) {
cleanup()
}
}
}
A finally blokk mindig végrehajtódik, beleértve a megszakítást is, és a NonCancellable garantálja, hogy a takarítás nem szakad meg.
Nem hívható suspend függvény közvetlenül callback-ből korutin létrehozása nélkül. Használja a suspendCoroutine-t vagy a suspendCancellableCoroutine-t a callback stílus korutinokhoz való igazításához.
Gyakran Ismételt Kérdések
Igen, technikailag egy suspend függvény nem hívhat más suspend függvényeket. A fordító egy egy állapotú (label 0) állapotgépet hoz létre. Azonban egy ilyen függvénynek nincs gyakorlati haszna — úgy hajtódik végre, mint egy hétköznapi, de többletköltséggel. Ne használja a suspend-et szükségtelenül.
A Kotlin biztosítja a kotlinx-coroutines-debug könyvtárat — DebugProbes-szel és korutin nyomkövető eszközökkel. Az Android Studio Arctic Fox verziójától kezdve van egy beépített Coroutines fül a Debugger-ben, amely megjeleníti az aktív korutinokat, azok állapotát és felfüggesztési pontjait.
Minden felfüggesztési pont létrehoz egy új állapotot az állapotgépben. A legtöbb alkalmazás számára egy pont többletköltsége nanoszekundumokban mérhető (Kotlin 1.9+). Csak amikor több tízezer pont van egy ciklusban, érdemes a műveleteket kombinálni vagy sequence/flow-t használni.
Kotlinban a suspend a függvény típusának módosítója, nem a visszatérési érték jelölője (mint az async C#-ban). Bármely suspend függvénynek lehet tetszőleges paramétere és visszatérési típusa, és a hívása szintaktikailag nem különbözik egy hétköznapi függvény hívásától — nincs await operátor a hívás helyén.
Használja a suspendCancellableCoroutine-t az adaptáláshoz. Belül átadja a callback regisztrációt, amely meghívja a continuation.resume()-et, és visszaad egy megszakítási tokent, ha a callback támogatja a leiratkozást. Ez a szabványos minta régi Android API-k becsomagolásához.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is