0Pricing
Kotlin Academy · Lekcja

SupervisorJob a Job: izolowanie błędów

Użyj SupervisorJob, aby błąd jednego zadania podrzędnego nie anulował jego rodzeństwa.

SupervisorJob a Job: izolowanie błędów to bezpłatna lekcja Kotlin Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Kotlin Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Kotlin Academy zawiera 4 lekcji w sumie.

Domyślne propagowanie błędów Job

W przypadku zwykłego obiektu Job błąd dziecka anuluje rodzica, który następnie anuluje wszystkie elementy równorzędne. Jeden błąd powoduje przerwanie całej hierarchii.

import kotlinx.coroutines.*
fun main() = runBlocking {
    try {
        coroutineScope {
            launch { delay(50); throw RuntimeException("child 1 failed") }
            launch { delay(1000); println("child 2 — never prints") }
        }
    } catch (e: RuntimeException) {
        println("Caught: ${e.message}")
    }
}

Przegląd SupervisorJob

SupervisorJob zmienia tę zasadę: błąd dziecka NIE anuluje elementów równorzędnych ani rodzica. Każde dziecko kończy się błędem niezależnie.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val supervisor = SupervisorJob()
    val scope = CoroutineScope(coroutineContext + supervisor)
    scope.launch { throw RuntimeException("child 1 failed") }
    scope.launch { delay(100); println("child 2 still runs") }
    delay(200)
    supervisor.cancel()
}

Konstruktor supervisorScope

supervisorScope { } tworzy zakres z obiektem SupervisorJob jako rodzicem. Jest to idiomatyczny sposób uruchamiania niezależnych elementów potomnych.

import kotlinx.coroutines.*
fun main() = runBlocking {
    supervisorScope {
        val job1 = launch {
            throw RuntimeException("job1 failed")
        }
        val job2 = launch {
            delay(100)
            println("job2 succeeded")
        }
        job1.join()  // wait for job1 (it failed)
        job2.join()  // job2 is unaffected
    }
}

Przechwytywanie błędów dzieci

W supervisorScope każde dziecko obsługuje własny wyjątek. Nieobsłużone wyjątki w dzieciach należy przechwytywać osobno dla każdego dziecka.

import kotlinx.coroutines.*
fun main() = runBlocking {
    supervisorScope {
        val result = async {
            delay(50)
            throw RuntimeException("async failed")
        }
        try {
            result.await()
        } catch (e: RuntimeException) {
            println("Caught from async: ${e.message}")
        }
        println("Scope continues")
    }
}

Job a SupervisorJob podczas tworzenia zakresu

Przekaż SupervisorJob() podczas tworzenia CoroutineScope dla usługi lub ViewModelu, który powinien działać mimo pojedynczych błędów dzieci.

import kotlinx.coroutines.*
class MyService {
    private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
    fun startTask(name: String) {
        scope.launch {
            if (name == "fail") throw RuntimeException("$name failed")
            println("$name done")
        }
    }
    fun stop() = scope.cancel()
}
fun main() = runBlocking {
    val svc = MyService()
    svc.startTask("fail")
    svc.startTask("ok")
    delay(100)
    svc.stop()
}

viewModelScope używa SupervisorJob

Androidowy zakres viewModelScope korzysta z obiektu SupervisorJob. Jedno nieudane uruchomienie nie anuluje pozostałych korutyn ViewModelu.

import kotlinx.coroutines.*
// Android ViewModel internals:
// val viewModelScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
// Each launch is independent — one failure doesn't crash the ViewModel
fun main() = runBlocking {
    println("viewModelScope = SupervisorJob + Main")
}

Dekompzycja równoległa z użyciem supervisorScope

Należy użyć supervisorScope + async do zadań wykonywanych równolegle, w których niektóre mogą zakończyć się niepowodzeniem bez wpływu na pozostałe.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val results = supervisorScope {
        val a = async { delay(50); "result-A" }
        val b = async { throw RuntimeException("B failed") }
        val c = async { delay(30); "result-C" }
        listOf(
            runCatching { a.await() },
            runCatching { b.await() },
            runCatching { c.await() }
        )
    }
    results.forEach { println(it) }
}

Kiedy używać zwykłego Job

Należy użyć zwykłego Job (coroutineScope), gdy wszystkie korutyny potomne muszą zakończyć się pomyślnie razem — jedno niepowodzenie powinno przerwać całą grupę. To jak transakcja: wszystko albo nic.

import kotlinx.coroutines.*
fun main() = runBlocking {
    try {
        coroutineScope {  // regular Job
            val a = async { delay(50); "A" }
            val b = async { throw RuntimeException("B failed") }
            println(a.await())
            println(b.await())  // throws — cancels a too
        }
    } catch (e: RuntimeException) {
        println("Transaction failed: ${e.message}")
    }
}

Propagacja wyjątków w supervisorScope

W supervisorScope wyjątek propaguje do zakresu tylko wtedy, gdy wydostanie się z najwyższego poziomu korutyny potomnej — tzn. gdy w korutynie potomnej nie ma try-catch. Korutyna nadrzędna go nie otrzymuje.

import kotlinx.coroutines.*
fun main() = runBlocking {
    supervisorScope {
        // Exception caught inside child — scope unaffected
        launch {
            try { throw RuntimeException("handled") }
            catch (e: Exception) { println("Child caught: ${e.message}") }
        }
        delay(100)
        println("Scope survived")
    }
}

CoroutineExceptionHandler z SupervisorJob

Należy umieścić CoroutineExceptionHandler w zakresie typu supervisor, aby rejestrować nieprzechwycone błędy korutyn potomnych lub na nie reagować bez powodowania awarii zakresu.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val handler = CoroutineExceptionHandler { _, e ->
        println("Uncaught: ${e.message}")
    }
    val scope = CoroutineScope(SupervisorJob() + handler)
    scope.launch { throw RuntimeException("unhandled failure") }
    scope.launch { delay(100); println("still running") }
    delay(200)
    scope.cancel()
}

Podsumowanie izolacji niepowodzeń

Job: jedno dziecko kończy się niepowodzeniem → wszystkie kończą się niepowodzeniem. SupervisorJob: dzieci kończą się niepowodzeniem niezależnie od siebie. Mechanizmu supervisor należy używać w usługach i ViewModels, a zwykłego Job w grupach transakcyjnych.

import kotlinx.coroutines.*
// Decision table:
// coroutineScope { }   -> Job: all-or-nothing
// supervisorScope { }  -> SupervisorJob: independent failures
// CoroutineScope(SupervisorJob()) -> long-lived service
fun main() = runBlocking { println("Pick the right job for the right scope") }

Szybkie sprawdzenie

Który zakres zapobiega anulowaniu rodzeństwa po niepowodzeniu korutyny potomnej?

Podsumowanie

Job: niepowodzenie propaguje się do rodzeństwa. SupervisorJob/supervisorScope: niepowodzenia są izolowane dla każdej korutyny potomnej. Należy używać supervisorScope do niezależnych zadań wykonywanych równolegle, a zwykłego coroutineScope w grupach transakcyjnych.

Często zadawane pytania

Czy lekcja „SupervisorJob a Job: izolowanie błędów” jest bezpłatna?

Tak — pełny tekst „SupervisorJob a Job: izolowanie błędów” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Kotlin Academy, przejdź na CoddyKit PRO. Kurs Kotlin Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „SupervisorJob a Job: izolowanie błędów”?

Użyj SupervisorJob, aby błąd jednego zadania podrzędnego nie anulował jego rodzeństwa. Ćwiczysz Kotlin Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Kotlin Academy?

Nie wymagamy żadnego doświadczenia. Kotlin Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.

Ile czasu zajmuje lekcja „SupervisorJob a Job: izolowanie błędów”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Kotlin Academy?

Tak. Każda lekcja Kotlin Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. SupervisorJob a Job: izolowanie błędów
  2. CoroutineExceptionHandler: globalny handler nieprzechwyconych wyjątków
  3. Propagacja wyjątków w async/await
  4. Projektowanie odpornych architektur korutyn
← Powrót do Kotlin Academy