Назад к вопросам
Middle
69
questionbank

Что произойдет с корутиной, когда возникает исключение?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

При возникновении исключения внутри корутины зависимость от типа исключения (и настроек диспетчера/контекста) определяет, как оно будет обработано.

  • Нерегулируемое исключение: Если исключение не перехвачено внутри корутины, оно propagate вверх по иерархии корутин до ближайшего родительского Job (или CoroutineScope). Поведение зависит от типа Job:

    • Job: По умолчанию, при возникновении исключения в дочерней корутине, родительский Job отменяется, что приводит к отмене всех его остальных дочерних корутин. Исключение перебрасывается выше.
    • SupervisorJob: Родительский SupervisorJob не отменяется при возникновении исключения в дочерней корутине. Только сама корутина, в которой произошло исключение, отменяется. Это полезно, когда нужно, чтобы отмена одной дочерней задачи не влияла на другие.
    • Корневой CoroutineScope: Если исключение достигает корневого CoroutineScope (например, GlobalScope или scope, созданный с помощью CoroutineScope без Job), оно передается в глобальный обработчик исключений CoroutineExceptionHandler. По умолчанию, если нет зарегистрированного обработчика, исключение логируется в System.err.
  • Перехваченное исключение: Если исключение перехвачено с помощью try-catch блока внутри корутины, оно обрабатывается локально, и корутина продолжает выполнение после блока catch, если это возможно. Исключение не распространяется вверх.

  • Исключения, связанных с отменой (например, CancellationException): Эти исключения являются особыми. Они используются для механизма отмены корутин и обычно игнорируются большинством обработчиков исключений по умолчанию. Они не приводят к фатальному сбою родительского Job по умолчанию.

  • Исключения в async: Корутина, запущенная через async, помещает исключение в свой Deferred. Исключение перебрасывается только при вызове .await() на этом Deferred. До этого момента исключение "скрыто".

import kotlinx.coroutines.*

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

    scope.launch {
        try {
            println("Coroutine 1 started")
            delay(100)
            throw RuntimeException("Something went wrong in Coroutine 1")
        } catch (e: Exception) {
            println("Coroutine 1 caught exception: ${e.message}")
        }
    }

    scope.launch {
        println("Coroutine 2 started")
        delay(500)
        println("Coroutine 2 finished")
    }

    delay(1000) // Ждем, чтобы увидеть вывод
    scope.cancel() // Отменяем scope
}

В этом примере, исключение в Coroutine 1 перехватывается, и Coroutine 2 продолжает выполнение, потому что используется SupervisorJob. Если бы вместо SupervisorJob использовался обычный Job, исключение в Coroutine 1 привело бы к отмене Coroutine 2.