Обдумывая исключения в сопрограммах - PullRequest
0 голосов
/ 29 мая 2019

В сопрограммах Kotlin вызов async не будет генерироваться, пока значение не будет получено. Однако, когда я запускаю функцию приостановки в блоке async, исключение не перехватывается, как я ожидал.

Вот пример кода:

fun start() {
    GlobalScope.launch {
        try {
            val def1 = async { doWork1() }
            val def2 = async { doWork2() }
            Log.d("test", "Result ${def1.await()}, ${def2.await()}")
        } catch (ex: Exception) {
            Log.e("test", "Error", ex)
        }
    }
}

private suspend fun doWork1(): Int {
    delay(1000L)
    throw Exception("work 1")
}

private suspend fun doWork2(): Int {
    delay(1000L)
    return 1
}

Это происходит сбой вместо перехвата исключения в блоке catch.

С другой стороны, это прекрасно работает

fun start() {
    GlobalScope.launch {
        try {
            val value = doWork1()
            Log.d("test", "Result: $value")
        } catch (ex: Exception) {
            Log.e("test", "Error", ex)
        }
    }
}

private suspend fun doWork1(): Int {
    delay(1000L)
    throw Exception("work 1")
}

Почему исключения не попадают в ловушку в первом примере?

1 Ответ

1 голос
/ 29 мая 2019

Второй случай прост: вы запускаете только 1 job с GlobalScope.launch, и exception чисто перехватывается.

Однако в первом случае ваши async вызовы создают рабочие места, которые являются дочернимивашего GlobalScope.launch.

Исключение фактически задерживается до вызова await(), но в этом случае оно не сбрасывается.Вместо этого async job передает исключение в родительское задание (launch), которое немедленно отменяет себя и все дочерние задания.Он пересылает исключение в свой ExceptionHandler, который по умолчанию не обработан.

Вы должны установить обработчик исключений в родительском контексте задания - это позволяет чистую обработку любого исключения в сопрограмме или ее дочерних элементах:

GlobalScope.launch(CoroutineExceptionHandler { coroutineContext, throwable ->
        println("Error: $throwable")
}) {
    val def1 = async { doWork1() }
    val def2 = async { doWork2() }
    println("Result ${def1.await()} ${def2.await()}") 
}

Подробнее об обработке исключений сопрограмм в Обработка исключений документов.

...