Sobes.tech
Middle

Mi történik a coroutine-nal, amikor kivétel lép fel?

sobes.tech MI

Válasz az MI-től

Amikor egy kivétel fordul elő egy coroutine belsejében, a kivétel típusától (és a dispatcher/kontekstus beállításaitól) függően dől el, hogyan kezeljük.

  • Kezeletlen kivétel: Ha a kivételt nem fogják el egy coroutine belsejében, akkor felfelé terjed a coroutine hierarchiában a legközelebbi Job vagy CoroutineScope-ig. A viselkedés a Job típusától függ:

    • Job: Alapértelmezés szerint, ha egy gyermek coroutine-ban kivétel történik, az szülő Job-ja törlődik, ami a többi gyermek coroutine törléséhez vezet. A kivétel felfelé terjed.
    • SupervisorJob: A szülő SupervisorJob nem törlődik, ha kivétel történik egy gyermek coroutine-ban. Csak az a coroutine törlődik, ahol a kivétel történt. Ez akkor hasznos, ha azt akarod, hogy egy gyermek feladat törlése ne befolyásolja a többit.
    • CoroutineScope gyökér: Ha a kivétel eléri a gyökér CoroutineScope-t (például, GlobalScope vagy egy CoroutineScope nélküli scope), akkor átadódik a globális CoroutineExceptionHandler-nek. Alapértelmezés szerint, ha nincs regisztrált kezelő, a kivétel a System.err-ben kerül naplózásra.
  • Elkapott kivétel: Ha a kivételt try-catch blokk segítségével fogják el a coroutine belsejében, akkor helyben kezelődik, és a coroutine folytatódik a catch blokk után, ha ez lehetséges. A kivétel nem terjed felfelé.

  • A kivételek, amelyek a törléssel kapcsolatosak (például CancellationException): Ezek különlegesek. A coroutine törlési mechanizmusához használják, és általában figyelmen kívül hagyják a legtöbb alapértelmezett kivételkezelő által. Alapértelmezés szerint nem okoznak fatális hibát a szülő Job-ban.

  • async-ben lévő kivételek: Az async-szal indított coroutine a kivételt a saját Deferred-jébe helyezi. A kivétel csak akkor terjed, amikor .await()-ot hívnak erre a Deferred-re. Eddig a kivétel "el van rejtve".

import kotlinx.coroutines.*

fun main() = runBlocking {
    val scope = CoroutineScope(SupervisorJob())

    scope.launch {
        try {
            println("Coroutine 1 indult")
            delay(100)
            throw RuntimeException("Valami rosszul sült el a Coroutine 1-ben")
        } catch (e: Exception) {
            println("Elkapott kivétel a Coroutine 1-ben: ${e.message}")
        }
    }

    scope.launch {
        println("Coroutine 2 indult")
        delay(500)
        println("Coroutine 2 befejeződött")
    }

    delay(1000) // Várunk, hogy lássuk az eredményt
    scope.cancel() // Scope leállítása
}

Ebben a példában a Coroutine 1-ben keletkezett kivétel el van kapva, és a Coroutine 2 folytatódik, mert SupervisorJob van használatban. Ha egy normál Job-ot használnánk helyette, a Coroutine 1-ben keletkezett kivétel a Coroutine 2 törléséhez vezetne.