Исключение не перехвачено в блоке запуска в coroutineScope - PullRequest
0 голосов
/ 31 января 2020

В test2 я обнаружил, что исключение не перехватывается, даже если я передаю обработчик исключения. Это работает только когда я запускаю сопрограмму, чтобы обернуть вызов, как test1 ... я ожидаю, что первый runSuspend вызов может перехватить исключение, и код может продолжить делать второй вызов runSuspend.

package coroutine.exceptions

import kotlinx.coroutines.*

fun log(msg: String) = println("$msg (${Thread.currentThread().name})")
val exceptionHandler = CoroutineExceptionHandler { _, e ->
    log(e.localizedMessage)
}

fun main() = runBlocking {

    val test1 = TestReuseCoroutineAfterException2("test1")
    test1.launch(exceptionHandler) {
        test1.runSuspend(true)
        // below won't be run because an exception is thrown above.
        delay(2000)
        test1.runSuspend(false)
        delay(2000)
    }.join()
    println()

    val test2 = TestReuseCoroutineAfterException2("test2")
    test2.runSuspend(true)
    // below won't be run because an exception is thrown above.
    delay(2000)
    test2.runSuspend(false)
    delay(2000)

    log("finished")
}


class TestReuseCoroutineAfterException2(private val testName: String) :
    CoroutineScope by CoroutineScope(Dispatchers.Default) {

    suspend fun runSuspend(throwException: Boolean) = coroutineScope {
        log("$testName: callSuspend - started")
        launch(exceptionHandler) {
            if (throwException)
                throw Exception("$testName: callSuspend - throw exception")
            else
                log("$testName: callSuspend - done")
        }
        log("$testName: callSuspend - ended")
    }
}

Выход

test1: вызов

Suspend - started (DefaultDispatcher-worker-2)
test1: callSuspend - ended (DefaultDispatcher-worker-2)
test1: callSuspend - throw exception (DefaultDispatcher-worker-1)

test2: callSuspend - started (main)
test2: callSuspend - ended (main)
Exception in thread "main" java.lang.Exception: test2: callSuspend - throw exception
    at coroutine.exceptions.TestReuseCoroutineAfterException2$callSuspend$2$1.invokeSuspend(TestReuseCoroutineAfterException2.kt:40)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
    at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.kt:116)
    at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:79)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:54)
    at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:36)
    at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    at coroutine.exceptions.TestReuseCoroutineAfterException2Kt.main(TestReuseCoroutineAfterException2.kt:10)
    at coroutine.exceptions.TestReuseCoroutineAfterException2Kt.main(TestReuseCoroutineAfterException2.kt)

Process finished with exit code 1

1 Ответ

3 голосов
/ 31 января 2020

CoroutineScope, созданный с помощью coroutineScope, не работает, если его дочерняя сопрограмма не работает. Если вы не хотите, чтобы ваша область не работала в этом случае, используйте supervisorScope builder:

suspend fun runSuspend(throwException: Boolean) = supervisorScope {

С этим изменением ваш код печатает:

test1: callSuspend - started (DefaultDispatcher-worker-1)
test1: callSuspend - ended (DefaultDispatcher-worker-1)
test1: callSuspend - throw exception (DefaultDispatcher-worker-1)
test1: callSuspend - started (DefaultDispatcher-worker-1)
test1: callSuspend - ended (DefaultDispatcher-worker-1)
test1: callSuspend - done (DefaultDispatcher-worker-1)

test2: callSuspend - started (main)
test2: callSuspend - ended (main)
test2: callSuspend - throw exception (main)
test2: callSuspend - started (main)
test2: callSuspend - ended (main)
test2: callSuspend - done (main)
finished (main)
...