SupervisorJob и Job: изоляция сбоев
Используйте SupervisorJob, чтобы сбой одного дочернего элемента не отменял соседние.
«SupervisorJob и Job: изоляция сбоев» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.
Распространение сбоев обычного задания
При использовании обычного Job сбой дочерней корутины отменяет родительскую, а она отменяет все соседние корутины. Один сбой нарушает работу всей иерархии.
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}")
}
}Обзор SupervisorJob
SupervisorJob меняет это правило: сбой дочерней корутины НЕ отменяет соседние корутины или родительскую. Каждая дочерняя корутина завершается независимо.
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()
}Построитель supervisorScope
supervisorScope { } создаёт область действия с SupervisorJob в качестве родительского задания. Это идиоматичный способ запуска независимых дочерних корутин.
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
}
}Обработка сбоев дочерних корутин
При использовании supervisorScope каждая дочерняя корутина обрабатывает собственное исключение. Необработанные исключения в дочерних корутинах необходимо перехватывать отдельно для каждой из них.
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 и SupervisorJob при создании области действия
Передавайте SupervisorJob() при создании CoroutineScope для сервиса или ViewModel, которые должны переживать отдельные сбои дочерних корутин.
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 использует SupervisorJob
Область действия Android viewModelScope работает на основе SupervisorJob. Один неудачный запуск не отменяет остальные корутины ViewModel.
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")
}Параллельная декомпозиция с помощью supervisorScope
Используйте supervisorScope вместе с async для параллельных задач, некоторые из которых могут завершиться с ошибкой, не влияя на остальные.
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) }
}Когда использовать обычное задание
Используйте обычный Job (coroutineScope), когда все дочерние корутины должны завершиться успешно вместе — одна ошибка должна прервать всю группу. Это похоже на транзакцию: либо выполняется всё, либо ничего.
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}")
}
}Распространение исключений в supervisorScope
В supervisorScope исключение передаётся в область видимости только в том случае, если оно выходит за пределы верхнего уровня дочерней корутины, то есть в дочерней корутине нет обработки через try-catch. Родительская корутина его не видит.
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 с SupervisorJob
Установите CoroutineExceptionHandler в супервизорной области видимости, чтобы записывать в журнал или обрабатывать необработанные сбои дочерних корутин, не допуская аварийного завершения области видимости.
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()
}Итоги изоляции сбоев
Задание: сбой одной дочерней корутины → сбой всех. SupervisorJob: дочерние корутины завершаются с ошибками независимо друг от друга. Используйте супервизор для сервисов и ViewModels, а обычный Job — для транзакционных групп.
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") }Быстрая проверка
Какая область видимости не позволяет сбою дочерней корутины отменить соседние корутины?
Повторение
Задание: сбой распространяется на соседние корутины. SupervisorJob/supervisorScope: сбои изолированы для каждой дочерней корутины. Используйте supervisorScope для параллельных независимых задач, а обычный coroutineScope — для транзакционных групп.
Изучай Kotlin с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 51
- Уроки
- 203
Часто задаваемые вопросы
Урок «SupervisorJob и Job: изоляция сбоев» бесплатный?
Да — полный текст урока «SupervisorJob и Job: изоляция сбоев» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.
Чему я научусь в уроке «SupervisorJob и Job: изоляция сбоев»?
Используйте SupervisorJob, чтобы сбой одного дочернего элемента не отменял соседние. Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Kotlin Academy?
Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «SupervisorJob и Job: изоляция сбоев»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Kotlin Academy?
Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- SupervisorJob и Job: изоляция сбоев
- CoroutineExceptionHandler: глобальный обработчик неперехваченных исключений
- Распространение исключений в async/await
- Проектирование устойчивых архитектур сопрограмм