5

I mają następujące oznaczenia:rzucanie wyjątku z 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() rzuca ServerException. Nie chcę obsługiwać tego tutaj, ale wyrzucam wyjątek od someFunc() do osoby dzwoniącej pod numer myFunc().

Odpowiedz

11

Twój kod sugeruje, że używasz wyniku operacja asynchroniczna później w ten sam sposób, więc będziesz mieć do czynienia z CompletionException tak, więc jednym ze sposobów radzenia sobie z nim, jest

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 
} 

Wszystkie wyjątki rzucane wewnątrz asynchronicznego przetwarzania Supplier dostanie, zapakowane w CompletionException podczas dzwonienia pod numer join, z wyjątkiem ServerException, który już został zawinięty w CompletionException.

Kiedy ponownie rzucać przyczynę CompletionException możemy zmierzyć niezaznaczone wyjątki, tj podklasy Error lub RuntimeException lub naszym zwyczajem sprawdzone wyjątku ServerException. Powyższy kod obsługuje wszystkie z nich z wieloma połowami, które będą je ponownie rzucać. Ponieważ zadeklarowany typ zwrotu: getCause() to Throwable, kompilator wymaga od nas obsługi tego typu, mimo że mamy już do czynienia z wszystkimi możliwymi typami. Prostym rozwiązaniem jest rzucenie tego, co jest niemożliwe, z zapakowanym w AssertionError.

Alternatywnie, możemy skorzystać z alternatywnej przyszłości wynik dla naszego niestandardowego wyjątek:

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 
} 

To rozwiązanie będzie ponownie rzucić wszystko „nieoczekiwane” throwables w ich zawinięte postaci, ale tylko rzut zwyczaj ServerException w oryginale Formularz przekazany za pośrednictwem przyszłości exception. Pamiętaj, że musimy upewnić się, że została zakończona a (na przykład wywołanie join()), zanim wyślemy zapytanie o przyszłość w postaci exception, aby uniknąć warunków wyścigu.

+0

to jest bardzo miłe ... – Eugene

+0

bardzo szczegółowa odpowiedź. –

2

myślę, że należy owinąć że do RuntimeException i rzucać że:

throw new RuntimeException(ex); 

czy wiele się małe narzędzie pomoże:

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

A następnie można unwrap że ..

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

muszę rzucać tylko 'ServerException'. – ayushgp

+1

@ayushgp Nie widzę, że dzieje się to z domyślnymi strumieniami, ponieważ nie zezwalają na zaznaczone wyjątki ... może być ok z owijaniem tego i rozpakowywaniem? – Eugene

+2

Wygląda na to, że twoja metoda 'go()' nigdy nie wyrzuciłaby niczego. Przypuszczam, że brakuje mu połączenia 'join()'. Ponadto 'Wrapper' nie dostarcza znacznie więcej niż to, co jest już dostępne w' Throwable.getCause() '. Nie zawijam wyjątku do innego bez ustawienia przyczyny, ponieważ łamie on konwencję i nie będzie drukował prawidłowych śladów stosu. –

0

Nawet jeśli odpowiedź innych jest bardzo dobra. ale daję ci inny sposób, aby rzucić sprawdzony wyjątek w CompletableFuture.

IF nie chcesz wywołać CompletableFuture w innym wątku, można za pomocą anonimowej klasy go obsłużyć, kod tak:

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

IF chcesz wywołać CompletableFuture w innym wątku, można również za pomocą anonimowej klasy go obsłużyć, ale uruchomić metodę runAsync:

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

Nie ma takiej potrzeby w anonimowej podklasie. Podklasa tylko marnuje zasoby. Zobacz także [tutaj] (https://stackoverflow.com/a/43767613/2711488) i [tutaj] (https://stackoverflow.com/a/28961083/2711488) ... – Holger

+0

@Holger, dziękuję, proszę pana. Piszę to tylko w mojej głowie. i zobaczę to później. –

+0

@ Holger Sir, znalazłem twoje dwie odpowiedzi są różne. i wolę twój pierwszy, którego użyłeś w tym pytaniu. ponieważ jest łatwy w użyciu i bardzo wyraźnie. –