2017-09-05 41 views
2

使用CompletableFuture的想法是因爲它提供了一個鏈,而前幾個步驟在最後一步使用它之前封裝了bean。因爲在這些步驟中可能會發生任何異常,並且使用exceptionally來處理錯誤。但是,exceptionally只接受Throwable參數,到目前爲止我還沒有找到一種方法來獲取這些封裝的bean。CompletableFuture異常破壞工作鏈

CompletableFuture.supplyAsync(this::msgSource) 
      .thenApply(this::sendMsg).exceptionally(this::errorHandler).thenAccept(this::saveResult) 
public List<Msg> msgSource() // take message from somewhere. 
public List<Msg> sendMsg(List<Msg>) // exceptions may happen like 403 or timeout 
public List<Msg> errorHandler() // set a success flag to false in Msg. 
public void saveResult(List<Msg>) // save send result like success or false in data center. 

在上面的例子中,註釋是工作流程。但是,由於errorHandler既不接受List<Msg>也不傳遞它,所以鏈被破壞。如何從msgSource獲得回報?

EDIT

public class CompletableFutureTest { 

    private static Logger log = LoggerFactory.getLogger(CompletableFutureTest.class); 
    public static void main(String[] args) { 
     CompletableFutureTest test = new CompletableFutureTest(); 
     CompletableFuture future = new CompletableFuture(); 
     future.supplyAsync(test::msgSource) 
      .thenApply(test::sendMsg).exceptionally(throwable -> { 
      List<String> list = (List<String>) future.join(); // never complete 
      return list; 
     }).thenAccept(test::saveResult); 
     try { 
      future.get(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } catch (ExecutionException e) { 
      e.printStackTrace(); 
     } 
    } 

    private List<String> saveResult(List<String> list) { 
     return list; 
    } 

    private List<String> sendMsg(List<String> list) { 
     throw new RuntimeException(); 
    } 

    public List<String> msgSource() { 
     List<String> result = new ArrayList<>(); 
     result.add("1"); 
     result.add("2"); 
     return result; 
    } 
} 

enter image description here

回答

3

意味着每個節點,即完成階段,使用前一個的結果。但是,如果前一階段的例外失敗,則沒有這樣的結果。這是您的sendMsg階段的一個特殊屬性,其結果與前一階段的價值相同,但對邏輯和API設計沒有影響。如果sendMsg因發生異常而失敗,它不會導致異常處理程序可以使用。

如果您想在例外情況下使用msgSource階段的結果,則不再有線性鏈。但CompletableFuture確實允許任意的依賴關係圖,而不僅僅是線性鏈模型,這樣你就可以表達它像

CompletableFuture<List<Msg>> source = CompletableFuture.supplyAsync(this::msgSource); 
source.thenApply(this::sendMsg) 
     .exceptionally(throwable -> { 
      List<Msg> list = source.join(); 
      for(Msg m: list) m.success = false; 
      return list; 
     }) 
     .thenAccept(this::saveResult); 

然而,有超過

CompletableFuture.runAsync(() -> { 
    List<Msg> list = msgSource(); 
    try { 
     list = sendMsg(list); 
    } catch(Throwable t) { 
     for(Msg m: list) m.success = false; 
    } 
    saveResult(list); 
}); 

沒有語義差別,也沒有優勢,它體現了相同的邏輯作爲普通的代碼流。

+0

不會爲所有的'Msg'設置'success = false'如果一個失敗? – Eugene

+2

@Eugene:當然。如果'sendMsg(列表)'失敗,則意味着整個列表失敗。這就是問題代碼的設計方式,它適用於'CompletableFuture'的使用,以及接收和返回'List'的方法。 – Holger

+0

如果'異常地'忘記了它以前的作品在消息中所做的所有操作,那麼它的返回將是無用的。但是你的第一個代碼段表明,有一種方法可以將前一項工作確實拿出來,只是前提條件是一個*知道哪些工作會導致異常*。我認爲是得到了引發異常的函數的參數(在本例中爲'List ')。有時候,這個鏈條可能會更長,嘗試捕捉它們中的每一個都可能是毫無意義的。 – Tiina