Sobes.tech
Middle

Co się dzieje z korutyną, gdy wystąpi wyjątek?

sobes.tech AI

Odpowiedź od AI

Gdy w ramach korutyny wystąpi wyjątek, zależność od typu wyjątku (i ustawień dispatcher/kontekstu) decyduje o tym, jak zostanie on obsłużony.

  • Nieobsłużony wyjątek: Jeśli wyjątek nie zostanie przechwycony w ramach korutyny, propaguje się on w górę hierarchii korutyn aż do najbliższego Job lub CoroutineScope. Zachowanie zależy od typu Job:

    • Job: Domyślnie, gdy w korutynie potomnej wystąpi wyjątek, nadrzędny Job zostaje anulowany, co powoduje anulowanie wszystkich pozostałych korutyn potomnych. Wyjątek jest propagowany w górę.
    • SupervisorJob: Nadrzędny SupervisorJob nie jest anulowany, gdy w korutynie potomnej wystąpi wyjątek. Anulowana jest tylko ta korutyna, w której wystąpił wyjątek. To jest przydatne, gdy chcesz, aby anulowanie jednej zadania potomnego nie wpływało na inne.
    • CoroutineScope główny: Jeśli wyjątek dotrze do głównego CoroutineScope (np. GlobalScope lub scope utworzony za pomocą CoroutineScope bez Job), jest przekazywany do globalnego obsługiwania wyjątków CoroutineExceptionHandler. Domyślnie, jeśli nie zarejestrowano obsługi, wyjątek jest logowany w System.err.
  • Przechwycony wyjątek: Jeśli wyjątek zostanie przechwycony za pomocą bloku try-catch wewnątrz korutyny, jest obsługiwany lokalnie, a korutyna kontynuuje działanie po bloku catch, jeśli to możliwe. Wyjątek nie jest propagowany w górę.

  • Wyjątki związane z anulowaniem (np. CancellationException): Są to wyjątki specjalne. Używa się ich do mechanizmu anulowania korutyn i są one zazwyczaj ignorowane przez większość domyślnych obsługi wyjątków. Nie powodują one krytycznego błędu nadrzędnego Job.

  • Wyjątki w async: Korutyna uruchomiona za pomocą async umieszcza wyjątek w swoim Deferred. Wyjątek jest propagowany tylko podczas wywołania .await() na tym Deferred. Do tego momentu wyjątek jest "ukryty".

import kotlinx.coroutines.*

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

    scope.launch {
        try {
            println("Korutyna 1 rozpoczęta")
            delay(100)
            throw RuntimeException("Coś poszło nie tak w Korutynie 1")
        } catch (e: Exception) {
            println("Przechwycono wyjątek w Korutynie 1: ${e.message}")
        }
    }

    scope.launch {
        println("Korutyna 2 rozpoczęta")
        delay(500)
        println("Korutyna 2 zakończona")
    }

    delay(1000) // Czekamy, aby zobaczyć wynik
    scope.cancel() // Anulujemy scope
}

W tym przykładzie, wyjątek w Korutynie 1 jest przechwytywany, a Korutyna 2 kontynuuje działanie, ponieważ używany jest SupervisorJob. Gdyby użyto zwykłego Job, wyjątek w Korutynie 1 spowodowałby anulowanie Korutyny 2.