Sobes.tech
Middle

Ce se întâmplă cu o corutină atunci când apare o excepție?

sobes.tech AI

Răspuns de la AI

Când apare o excepție în interiorul unei coroutine, dependența de tipul excepției (și setările dispatcher/conținut) determină modul în care aceasta va fi gestionată.

  • Excepție necontrolată: Dacă excepția nu este capturată în interiorul coroutinei, aceasta se propagă în sus în ierarhia coroutinelor până la cel mai apropiat Job sau CoroutineScope. Comportamentul depinde de tipul de Job:

    • Job: În mod implicit, atunci când apare o excepție într-o coroutine fiică, Job-ul părinte este anulat, ceea ce duce la anularea tuturor celorlalte coroutines sale fiice. Excepția se propagă în sus.
    • SupervisorJob: SupervisorJob părinte nu este anulat atunci când apare o excepție într-o coroutine fiică. Doar coroutinea în care a avut loc excepția este anulată. Acest lucru este util atunci când vrei ca anularea unei sarcini fiice să nu afecteze celelalte.
    • CoroutineScope rădăcină: Dacă excepția ajunge la CoroutineScope rădăcină (de exemplu, GlobalScope sau un scope creat cu CoroutineScope fără Job), ea este transmisă handler-ului global de excepții CoroutineExceptionHandler. În mod implicit, dacă nu există un handler înregistrat, excepția este înregistrată în System.err.
  • Excepție capturată: Dacă excepția este capturată cu ajutorul unui bloc try-catch în interiorul coroutinei, ea este gestionată local și coroutine continuă execuția după blocul catch, dacă este posibil. Excepția nu se propagă în sus.

  • Excepții legate de anulare (de exemplu, CancellationException): Aceste excepții sunt speciale. Ele sunt folosite pentru mecanismul de anulare a coroutinelor și sunt de obicei ignorate de majoritatea handler-ilor de excepții implicit. Ele nu duc la o eroare fatală a Job-ului părinte implicit.

  • Excepții în async: Coroutinele lansate cu async plasează excepția în Deferred-ul său. Excepția este propagată doar atunci când se apelează .await() pe acest Deferred. Până atunci, excepția este "ascunsă".

import kotlinx.coroutines.*

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

    scope.launch {
        try {
            println("Coroutine 1 început")
            delay(100)
            throw RuntimeException("Ceva nu a mers bine în Coroutine 1")
        } catch (e: Exception) {
            println("Excepție capturată în Coroutine 1: ${e.message}")
        }
    }

    scope.launch {
        println("Coroutine 2 început")
        delay(500)
        println("Coroutine 2 terminat")
    }

    delay(1000) // Așteptăm pentru a vedea output-ul
    scope.cancel() // Anulăm scope-ul
}

În acest exemplu, excepția din Coroutine 1 este capturată, iar Coroutine 2 continuă execuția deoarece se folosește SupervisorJob. Dacă s-ar folosi în loc un Job obișnuit, excepția din Coroutine 1 ar duce la anularea Coroutine 2.