5

Je le code suivant:Lancer exception de CompletableFuture

// How to throw the ServerException? 
public void myFunc() throws ServerException{ 
    // Some code 
    CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> { 
     try { 
      return someObj.someFunc(); 
     } catch(ServerException ex) { 
      // throw ex; gives an error here. 
     } 
    })); 
    // Some code 
} 

someFunc() jette un ServerException. Je ne veux pas gérer cela ici, mais jeter l'exception de someFunc() à l'appelant de myFunc().

Répondre

11

Votre code suggère que vous utilisez le résultat de la opération asynchrone plus tard dans la même méthode, vous devrez donc faire face à CompletionException de toute façon, une façon de traiter avec elle, est

public void myFunc() throws ServerException { 
    // Some code 
    CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> { 
     try { return someObj.someFunc(); } 
     catch(ServerException ex) { throw new CompletionException(ex); } 
    }); 
    // Some code running in parallel to someFunc() 

    A resultOfA; 
    try { 
     resultOfA = a.join(); 
    } 
    catch(CompletionException ex) { 
     try { 
      throw ex.getCause(); 
     } 
     catch(Error|RuntimeException|ServerException possible) { 
      throw possible; 
     } 
     catch(Throwable impossible) { 
      throw new AssertionError(impossible); 
     } 
    } 
    // some code using resultOfA 
} 

Toutes les exceptions lancées à l'intérieur du traitement asynchrone du Supplier va s'enveloppées dans un CompletionException lors de l'appel join, sauf le ServerException nous avons déjà enveloppé dans un CompletionException.

Quand nous sommes-jeter la cause de la CompletionException, nous pouvons faire face à des exceptions non vérifiées, à savoir les sous-classes de Error ou RuntimeException, ou notre coutume vérifié exception ServerException. Le code ci-dessus gère chacun d'entre eux avec un multi-catch qui les relancera. Puisque le type de retour déclaré de getCause() est Throwable, le compilateur exige que nous gérions ce type malgré que nous ayons déjà traité tous les types possibles. La solution directe est de jeter ce jetable réellement impossible enveloppé dans un AssertionError.

Sinon, nous pourrions utiliser un futur résultat alternatif pour notre exception personnalisée:

public void myFunc() throws ServerException { 
    // Some code 
    CompletableFuture<ServerException> exception = new CompletableFuture<>(); 
    CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> { 
     try { return someObj.someFunc(); } 
     catch(ServerException ex) { 
      exception.complete(ex); 
      throw new CompletionException(ex); 
     } 
    }); 
    // Some code running in parallel to someFunc() 

    A resultOfA; 
    try { 
     resultOfA = a.join(); 
    } 
    catch(CompletionException ex) { 
     if(exception.isDone()) throw exception.join(); 
     throw ex; 
    } 

    // some code using resultOfA 
} 

Cette solution re-jeter tous les « inattendus » Throwables sous leur forme enveloppée, mais seulement jeter la coutume ServerException dans sa version originale formulaire passé via le exception avenir. Notez que nous devons nous assurer que a a été complété (comme l'appel join() d'abord), avant que nous demandions le futur exception, pour éviter les conditions de course.

+0

c'est tellement agréable ... – Eugene

+0

réponse très détaillée. –

2

Je pense que vous devriez envelopper que dans un RuntimeException et jeter que:

throw new RuntimeException(ex); 

Ou beaucoup être un petit utilitaire aideriez:

static class Wrapper extends RuntimeException { 

    private Wrapper(Throwable throwable) { 
     super(throwable); 
    } 

    public static Wrapper wrap(Throwable throwable) { 
     return new Wrapper(throwable); 
    } 

    public Throwable unwrap() { 
     return getCause(); 
    } 
} 


public static void go() { 
    CompletableFuture<String> a = CompletableFuture.supplyAsync(() -> { 
     try { 
      throw new Exception("Just because"); 
     } catch (Exception ex) { 
      throw Wrapper.wrap(ex); 
     } 
    }); 

    a.join(); 
} 

Et vous pouvez alors unwrap que ..

try { 
     go(); 
} catch (Wrapper w) { 
     throw w.unwrap(); 
} 
+0

J'ai besoin de jeter un 'ServerException' seulement. – ayushgp

+1

@ayushgp Je ne vois pas ce qui se passe avec les flux par défaut, car ils ne permettent pas les exceptions vérifiées ... peut-être que vous seriez ok avec emballage celui-là et que déballer? – Eugene

+2

Il semble que votre méthode 'go()' ne lancerait rien. Je suppose qu'il manque un appel 'join()'. En outre, 'Wrapper' ne fournit pas beaucoup plus que ce qui est déjà disponible avec' Throwable.getCause() '. Je ne voudrais pas envelopper une exception dans un autre sans définir la cause, car il rompt la convention et il n'imprimera pas les bons stacktraces. –

0

Même si la réponse de l'autre est très agréable. mais je vous donne un autre moyen de lancer une exception cochée dans CompletableFuture.

SI vous ne voulez pas invoquer un CompletableFuture dans un autre thread, vous pouvez en utilisant une classe anonyme pour le manipuler, le code comme ceci:

CompletableFuture<A> a = new CompletableFuture<A>() {{ 
    try { 
     complete(someObj.someFunc()); 
    } catch (ServerException ex) { 
     completeExceptionally(ex); 
    } 
}}; 

SI vous voulez invoquer une CompletableFuture dans un autre thread, vous pouvez également en utilisant une classe anonyme pour gérer, mais la méthode dirigée par runAsync:

CompletableFuture<A> a = new CompletableFuture<A>() {{ 
    CompletableFuture.runAsync(() -> { 
     try { 
      complete(someObj.someFunc()); 
     } catch (ServerException ex) { 
      completeExceptionally(ex); 
     } 
    }); 
}}; 
+4

Il n'est pas nécessaire de le faire dans une sous-classe anonyme. La sous-classe ne gaspille que des ressources. Voir aussi [ici] (https://stackoverflow.com/a/43767613/2711488) et [ici] (https://stackoverflow.com/a/28961083/2711488) ... – Holger

+0

@Holger merci, monsieur. Je l'écris seulement dans mon esprit. et je le verrai plus tard. –

+0

@Holger monsieur, j'ai trouvé vos deux réponses sont différentes. et je préfère votre premier que vous avez utilisé dans cette question. parce qu'il est facile à utiliser et très clairement. –