Sobes.tech
Middle

Was passiert mit einer Coroutine, wenn eine Ausnahme auftritt?

sobes.tech KI

Antwort von AI

Wenn innerhalb einer Coroutine eine Ausnahme auftritt, bestimmt die Abhängigkeit vom Ausnahmetyp (und den Einstellungen des Dispatchers/Kontexts), wie sie behandelt wird.

  • Unbehandelte Ausnahme: Wenn die Ausnahme innerhalb der Coroutine nicht abgefangen wird, wird sie nach oben in der Coroutine-Hierarchie bis zum nächsten Job oder CoroutineScope propagiert. Das Verhalten hängt vom Job-Typ ab:

    • Job: Standardmäßig wird bei einer Ausnahme in einer Kind-Coroutine der übergeordnete Job abgebrochen, was zum Abbruch aller anderen Kind-Coroutines führt. Die Ausnahme wird nach oben weitergeleitet.
    • SupervisorJob: Der übergeordnete SupervisorJob wird bei einer Ausnahme in einer Kind-Coroutine nicht abgebrochen. Nur die Coroutine, in der die Ausnahme auftrat, wird abgebrochen. Das ist nützlich, wenn das Abbrechen einer Kind-Aufgabe die anderen nicht beeinflussen soll.
    • CoroutineScope-Wurzel: Wenn die Ausnahme den Wurzel-CoroutineScope erreicht (z.B. GlobalScope oder ein mit CoroutineScope ohne Job erstellter Scope), wird sie an den globalen CoroutineExceptionHandler weitergeleitet. Standardmäßig wird die Ausnahme in System.err protokolliert, wenn kein Handler registriert ist.
  • Abgefangene Ausnahme: Wenn die Ausnahme mit einem try-catch-Block innerhalb der Coroutine abgefangen wird, wird sie lokal behandelt, und die Coroutine setzt die Ausführung nach dem catch-Block fort, falls möglich. Die Ausnahme wird nicht nach oben weitergeleitet.

  • Ausnahmen im Zusammenhang mit Abbruch (z.B. CancellationException): Diese Ausnahmen sind speziell. Sie werden für den Abbruchmechanismus von Coroutines verwendet und werden von den meisten Standard-Exception-Handlern ignoriert. Sie führen standardmäßig nicht zu einem fatalen Fehler des übergeordneten Job.

  • Ausnahmen bei async: Eine mit async gestartete Coroutine legt die Ausnahme in ihrem Deferred ab. Die Ausnahme wird nur bei einem Aufruf von .await() auf diesem Deferred weitergeleitet. Bis dahin ist die Ausnahme "versteckt".

import kotlinx.coroutines.*

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

    scope.launch {
        try {
            println("Coroutine 1 gestartet")
            delay(100)
            throw RuntimeException("Etwas ist schiefgelaufen in Coroutine 1")
        } catch (e: Exception) {
            println("Coroutine 1 abgefangene Ausnahme: ${e.message}")
        }
    }

    scope.launch {
        println("Coroutine 2 gestartet")
        delay(500)
        println("Coroutine 2 beendet")
    }

    delay(1000) // Warten, um die Ausgabe zu sehen
    scope.cancel() // Scope abbrechen
}

In diesem Beispiel wird die Ausnahme in Coroutine 1 abgefangen, und Coroutine 2 setzt die Ausführung fort, weil SupervisorJob verwendet wird. Wenn stattdessen ein normaler Job verwendet würde, würde die Ausnahme in Coroutine 1 zum Abbruch von Coroutine 2 führen.