Sobes.tech
Middle

Какво се случва с корутината, когато възникне изключение?

sobes.tech AI

Отговор от AI

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

  • Нерегулирано изключение: Ако изключението не бъде хванато вътре в корутината, то се пропагира нагоре по йерархията на корутините до най-близкия родителски 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("Корутината 1 започна")
            delay(100)
            throw RuntimeException("Нещо се обърка в Корутината 1")
        } catch (e: Exception) {
            println("Хваната изключение в Корутината 1: ${e.message}")
        }
    }

    scope.launch {
        println("Корутината 2 започна")
        delay(500)
        println("Корутината 2 приключи")
    }

    delay(1000) // Изчакваме, за да видим изхода
    scope.cancel() // Отменяме scope
}

В този пример, изключението в Корутината 1 е хванато, и Корутината 2 продължава изпълнението си, защото е използван SupervisorJob. Ако беше използван обикновен Job, изключението в Корутината 1 щеше да доведе до отменяне на Корутината 2.