Почему при использовании supervisorScope, CoroutineExceptionHandler не получает ошибку?
Я тестирую всякие примеры касательно exception handling и вот у меня есть очень простой пример
fun main() {
val handler = CoroutineExceptionHandler { coroutineContext, throwable ->
println("handled exception: $throwable")
}
val scope = CoroutineScope(Job() + handler)
scope.launch {
val firstResultDeferred = async {
println("print FIRST")
throw RuntimeException()
}
val secondResultDeferred = async {
println("print SECOND")
"SECOND"
}
val thirdResultDeferred = async {
println("print THIRD")
"THIRD"
}
val firstResult = try {
firstResultDeferred.await()
} catch (e: Exception) {
println("1 exception: $e")
null
}
val secondResult = try {
secondResultDeferred.await()
} catch (e: Exception) {
println("2 exception: $e")
null
}
val thirdResult = try {
thirdResultDeferred.await()
} catch (e: Exception) {
println("3 exception: $e")
null
}
val resultList = listOf(firstResult, secondResult, thirdResult)
println("RESULT IS: ${resultList.joinToString(", ")}")
}
Thread.sleep(2000)
}
Запускается 3 асинка где в первом ошибка, вот такой лог получается
print FIRST
1 exception: java.lang.RuntimeException
2 exception: kotlinx.coroutines.JobCancellationException: Parent job is Cancelling; job=StandaloneCoroutine{Cancelling}@69ea4681
3 exception: kotlinx.coroutines.JobCancellationException: Parent job is Cancelling; job=StandaloneCoroutine{Cancelling}@69ea4681
RESULT IS: null, null, null
handled exception: java.lang.RuntimeException
Process finished with exit code 0
Вроде как все предсказуемо, мы видим первый принт print FIRST потом ошибка которая отлавливается try/catch -> 1 exception: java.lang.RuntimeException, поскольку это обычный Job то два других асинка отменяются и это тоже отлавливается.
Интересный момент в том, что сама ошибка которая происходит в первом асинке передается вверх по иерархии в launch где отлавливается handlerом и мы видим последний лог handled exception: java.lang.RuntimeException. Тут вроде как все логично получается.
Но если я хочу, чтоб при ошибке в одном асинке други продолжали свою работу, мне нужно поменять скоуп на supervisorScope, вот так
fun main() {
val handler = CoroutineExceptionHandler { coroutineContext, throwable ->
println("handled exception: $throwable")
}
val scope = CoroutineScope(Job() + handler)
scope.launch {
supervisorScope {
val firstResultDeferred = async {
println("print FIRST")
throw RuntimeException()
}
val secondResultDeferred = async {
println("print SECOND")
"SECOND"
}
val thirdResultDeferred = async {
println("print THIRD")
"THIRD"
}
val firstResult = try {
firstResultDeferred.await()
} catch (e: Exception) {
println("1 exception: $e")
null
}
val secondResult = try {
secondResultDeferred.await()
} catch (e: Exception) {
println("2 exception: $e")
null
}
val thirdResult = try {
thirdResultDeferred.await()
} catch (e: Exception) {
println("3 exception: $e")
null
}
val resultList = listOf(firstResult, secondResult, thirdResult)
println("RESULT IS: ${resultList.joinToString(", ")}")
}
}
Thread.sleep(2000)
}
и лог получается вот такой
print FIRST
print SECOND
1 exception: java.lang.RuntimeException
print THIRD
RESULT IS: null, SECOND, THIRD
Process finished with exit code 0
Вроде тоже все предсказуемо и дейсвительно в результате два оставшихся асинка дорабатывают, НО вопрос - куда делась вот эта строка - handled exception: java.lang.RuntimeException, почему ошибка не предается вверх к launch и не отлавливается хендлером?