IMO, вариант 1) усложняет использование API, поскольку для сообщения об ошибках будет два разных пути:
- «Синхронные» исключения, когда метод завершает генерируемое исключение.
- «Асинхронные» исключения, когда метод возвращает CF, что завершается исключением. Обратите внимание, что избежать этого случая невозможно, потому что всегда будут ситуации, когда ошибки обнаруживаются только после запуска асинхронного пути (например, тайм-ауты).
Теперь программист должен убедиться, что оба эти пути правильно обработаны, а не один.
Интересно также отметить, что поведение как для C #, так и для Javascript заключается в том, чтобы всегда сообщать об исключениях, выбрасываемых внутри тела функции async
через возвращаемый Task
/ Promise
, даже для исключений, выданных перед первым await
и никогда после завершения вызова функции async
с исключением.
То же самое верно и для сопрограмм Котлина, даже при использовании диспетчера Unconfined
class SynchronousExceptionExamples {
@Test
fun example() {
log.info("before launch")
val job = GlobalScope.launch(Dispatchers.Unconfined) {
log.info("before throw")
throw Exception("an-error")
}
log.info("after launch")
Thread.sleep(1000)
assertTrue(job.isCancelled)
}
}
будет производить
6 [main] INFO SynchronousExceptionExamples - before launch
73 [main @coroutine#1] INFO SynchronousExceptionExamples - before throw
(...)
90 [main] INFO SynchronousExceptionExamples - after launch
Обратите внимание, что исключение возникает в потоке main
, однако launch
заканчивается правильным Job
.