Kotlin async в сопрограмме: исключение поймано и все еще распространяется?

Я запускаю сопрограмму в SupervisorJob с блоком try/catch, окружающим только вызовы await. Исключение из асинхронного блока перехватывается try/catch, но все равно распространяется, и приложение аварийно завершает работу.

Вот что у меня есть:

CoroutineScope(Dispatchers.IO + SupervisorJob()).launch {
            val a = async {
                delay(500)
                throw Exception("excep a")
                2
            }
            val b = async {
                delay(500)
                3
            }
            try {
                println(a.await() + b.await())
            } catch (e: Exception) {
                println("exception: ${e.message}")
            }
        }

Это то, что я получаю (обратите внимание, что кроме a попадается):

exception: excep a
Exception in thread "DefaultDispatcher-worker-3 @coroutine#1" java.lang.Exception: excep a
    at com.example.app.AuthTest$co2$1$a$1.invokeSuspend(AuthTest.kt:314)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)

person hoshiKuzu    schedule 23.05.2021    source источник
comment
Ваш launch является только дочерним элементом supervisorJob, хотя и не является им самим, поэтому он отменяется и повторно выдает исключение, созданное a. Возможно, вы хотите, чтобы supervisorScope было вложено в ваш launch? См. stackoverflow.com/a/53240861/9241978.   -  person Pawel    schedule 23.05.2021
comment
Кроме того, вы почти всегда должны использовать либо coroutineScope, либо supervisorScope с async, потому что правильный способ использования async состоит в том, чтобы распараллелить задачу, а не запускать длительное фоновое задание. Распараллеливание задачи означает, что она не должна завершаться до тех пор, пока не будут выполнены дочерние async задачи.   -  person Marko Topolnik    schedule 24.05.2021


Ответы (1)


Механизм обработки исключений для supervisorJob и Job отличается. Для контрольных задач передача исключений может быть только из родительской области в дочернюю область, а направление распространения исключения — одностороннее. Следовательно, необходимо обрабатывать исключения самостоятельно для контроля области открытия сопрограммы.

    CoroutineScope(Dispatchers.IO + SupervisorJob()).launch(CoroutineExceptionHandler { _, throwable ->
        println("$throwable")
    }) {
        val a = async {
            delay(500)
            throw Exception("excep a")
            2
        }
        val b = async {
            delay(500)
            3
        }
        println(a.await() + b.await())
    }

Это исключение будет обрабатываться в задании верхнего уровня.

person Future Deep Gone    schedule 24.05.2021