5
votes

Est-il raisonnable de lever une exception à partir d'une méthode asynchrone?

En développant en Java une méthode asynchrone avec un type de retour CompletableFuture , nous nous attendons à ce que le CF résultant se termine normalement ou exceptionnellement selon que cette méthode réussit ou échoue.

Cependant, considérez par exemple que ma méthode écrit dans un AsynchronousChannel et a obtenu une exception ouvrant ce canal. Il n'a même pas commencé à écrire. Donc, dans ce cas, je tente simplement de laisser l'exception couler vers l'appelant. Est-ce correct?

Bien que l'appelant devra gérer 2 scénarios d'échec: 1) exception, ou 2) promesse rejetée.

Ou bien ma méthode doit-elle intercepter cette exception et renvoyer une promesse rejetée à la place?


0 commentaires

3 Réponses :


2
votes

Je pense que les deux sont des designs valables. Datastax a en fait commencé sa conception avec la première approche, où l'emprunt d'une connexion était bloquant, et est passé au modèle entièrement asynchrone ( https://docs.datastax.com/en/developer/java-driver/3.5/upgrade_guide/#3-0-4 )

En tant qu'utilisateur du pilote java datastax, j'étais très satisfait du correctif, car il a changé l'API pour être vraiment non bloquant (même ouvrir un canal, dans votre exemple, a un coût).

Mais je ne pense pas qu'il y ait du bien et du mal ici ...


0 commentaires

1
votes

Cela ne fait pas une grande différence du point de vue des appelants. Dans les deux cas, la cause de l'exception sera visible, qu'elle soit lancée depuis la méthode ou en appelant get () sur le futur complet.

Je dirais peut-être qu'une exception levée par le futur complétable devrait être une exception du calcul asynchrone et ne pas manquer de démarrer ce calcul.


0 commentaires

2
votes

IMO, option 1) rend l'API plus difficile à utiliser car il y aura deux chemins différents pour communiquer les erreurs:

  1. Exceptions "synchrones", où la méthode met fin à une exception levée.
  2. Exceptions "asynchrones", où la méthode renvoie un CF, qui se termine par une exception. Notez qu'il est impossible d'éviter ce cas, car il y aura toujours des situations où les erreurs ne seront trouvées qu'après le démarrage du chemin asynchrone (par exemple, les délais d'attente).

Le programmeur doit maintenant s'assurer que ces deux chemins sont correctement gérés, au lieu d'un seul.

Il est également intéressant d'observer que le comportement pour C # et Javascript est de toujours signaler les exceptions lancées dans le corps d'une fonction async via la Task / Promise renvoyée, même pour les exceptions levées avant le premier wait , et jamais en terminant l'appel de fonction async par une exception.

Il en va de même pour les coroutines de Kotlin, même en utilisant le code Unconfined > dispatcher

6 [main] INFO SynchronousExceptionExamples - before launch
73 [main @coroutine#1] INFO SynchronousExceptionExamples - before throw
(...)
90 [main] INFO SynchronousExceptionExamples - after launch

produira

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)
    }
}

Notez que l'exception se produit dans le fil de discussion principal , cependant le lancement se termine par un Job approprié .


0 commentaires