Разумно ли выбрасывать исключение из асинхронного метода? - PullRequest
0 голосов
/ 16 января 2019

Разрабатывая в Java асинхронный метод с типом возврата CompletableFuture, мы ожидаем, что результирующий CF завершится нормально или исключительно в зависимости от того, будет ли этот метод успешным или нет.

Тем не менее, рассмотрим, например, что мой метод пишет в AsynchronousChannel и получил исключение, открывающее этот канал. Он даже не начал писать. Итак, в этом случае я ставлю палатку, чтобы позволить исключению перетекать к вызывающей стороне. Это правильно?

Хотя вызывающей стороне придется иметь дело с 2 сценариями сбоя: 1) исключение или 2) отклоненное обещание.

Или, наоборот, должен ли мой метод перехватить это исключение и вместо этого вернуть отклоненное обещание?

Ответы [ 3 ]

0 голосов
/ 16 января 2019

Это не имеет большого значения с точки зрения абонентов. В любом случае будет видна причина исключения, независимо от того, было ли оно выброшено из метода или из вызова get () в завершаемом будущем.

Возможно, я бы поспорил, что исключение, создаваемое завершаемым будущим, должно быть исключением из асинхронных вычислений и не должно начинать эти вычисления.

0 голосов
/ 19 января 2019

IMO, вариант 1) усложняет использование API, поскольку для сообщения об ошибках будет два разных пути:

  1. «Синхронные» исключения, когда метод завершает генерируемое исключение.
  2. «Асинхронные» исключения, когда метод возвращает 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.

0 голосов
/ 16 января 2019

Я думаю, что оба допустимых дизайна. Datastax фактически начал свою разработку с первого подхода, когда заимствование соединения блокировалось, и переключился на полностью асинхронную модель (https://docs.datastax.com/en/developer/java-driver/3.5/upgrade_guide/#3-0-4)

Как пользователь jav-драйвера datastax, я был очень доволен исправлением, так как оно изменило api на действительно неблокирующее (даже открытие канала, в вашем примере, имеет свою стоимость).

Но я не думаю, что здесь есть правильное и неправильное ...

...