Когда вы генерируете исключение в области сопрограмм, можно ли повторно использовать область сопрограмм?
У меня были проблемы с выяснением обработки ошибок с сопрограммами, которые я сузил до этого модульного теста со следующими шагами:
- Я создаю область сопрограммы с любым диспетчером.
- Я выбрасываю исключение в любом месте этой области в асинхронном блоке (или даже во вложенном асинхронном блоке).
- Я вызываю ожидание возвращенного отложенного значения и обрабатываю исключение.
Это все хорошо. Однако, когда я пытаюсь использовать ту же область сопрограмм для запуска новой сопрограммы, это всегда завершается исключительно с тем же исключением.
Вот тест:
fun `when you throw an exception in a coroutine scope, is the coroutine scope dead?`() { val parentJob = Job() val coroutineScope = CoroutineScope(parentJob + Dispatchers.Default) val deferredResult = coroutineScope.async { throw IllegalStateException() } runBlocking { try { deferredResult.await() } catch (e: IllegalStateException) { println("We caught the exception. Good.") } try { coroutineScope.async { println("we can still use the scope") }.await() } catch (e: IllegalStateException) { println("Why is this same exception still being thrown?") } } }
Вот результат теста:
We caught the exception. Good.
Why is this same exception still being thrown?
Почему это происходит?
- Насколько я понимаю, вы можете нормально обрабатывать исключения и восстанавливать их с помощью сопрограмм.
Как я должен иметь дело с исключениями?
- Нужно ли создавать новый coroutineScope?
- Могу ли я никогда не выдавать исключения, если я хочу продолжать использовать один и тот же coroutineScope?
- Должен ли я вернуться
Either<Result, Exception>
? - Я пытался использовать CoroutineExceptionHandler, но я все еще получаю те же результаты.
Обратите внимание, что я использую Kotlin 1.3
1 ответ
Когда вы запускаете сопрограмму в области видимости (используя либо async
или же launch
), то сбой сопрограммы по умолчанию отменяет эту область, чтобы быстро отменить все другие дочерние элементы. Такой дизайн позволяет избежать висячих и потерянных исключений.
Общий совет здесь:
Не использовать
async
/await
если вам действительно не нужен параллелизм. Когда вы разрабатываете свой код с функциями приостановки, нет особой необходимости использоватьasync
а такжеawait
,Если вам нужно параллельное выполнение, следуйте шаблону:
coroutineScope { val d1 = async { doOne() } val d2 = async { doTwo() } ... // retrieve and process results process(d1.await(), d2.await(), .... ) }
Если вам нужно обработать сбой параллельной операции, то поставьте try { ... } catch { ... }
вокруг coroutineScope { ... }
поймать сбой в любой из одновременно выполняемых операций.
- Есть дополнительные продвинутые механизмы (например,
SupervisorJob
), которые позволяют детализированную обработку исключений. Вы можете прочитать больше в документации https://kotlinlang.org/docs/reference/coroutines/exception-handling.html