簡體   English   中英

CompletableFuture.runAsync 吞咽異常

[英]CompletableFuture.runAsync Swallowing Exceptions

早上好,

我不太了解 CompletableFutures(我是一位經驗豐富的開發人員,但我覺得它們不是特別直觀!)。

鑒於以下代碼段:

public CompletionStage<Void> leaveGame(GameService gameService)
{
  return gameService.deregister(playerName)
                    .exceptionally(t -> {
                      LOGGER.info("Could not deregister: {}", t.getMessage());
                      throw new CompletionException(t);
                    });
}

由單元測試調用:

@Test
public void shouldCompleteExceptionallyForFailedLeave()
{
  var failFlow = new CompletableFuture<Void>();
  failFlow.completeExceptionally(new Exception("TestNonExistentPlayer"));
  when(mockedGameService.deregister(any(String.class))).thenReturn(failFlow);

  try
  {
    player.leaveGame(mockedGameService).toCompletableFuture().get();
    fail("Exception should have been thrown");
  }
  catch (Exception e)
  {
    assertEquals(Exception.class, e.getCause().getClass());
  }
  verify(mockedGameService, times(1)).deregister(any(String.class));
}

gameService.deregister(...)以完成Exception並返回Exception

在上面的情況下,正如預期的那樣,觸發了異常分支,記錄了消息,並捕獲了單元測試中的異常,即不觸發fail(...)斷言。

但是,當我想在離開游戲之前運行 CompletionStage 時,例如:

public CompletionStage<Void> leaveGame(GameService gameService)
{
  return CompletableFuture.runAsync(() -> System.out.println("BLAH"))
                          .thenRun(() -> gameService.deregister(playerName)
                                                    .exceptionally(t -> {
                                                      LOGGER.info("Could not deregister: {}", t.getMessage());
                                                      throw new CompletionException(t);
                                                    }));
}

格外分支仍然觸發,但例外是現在不被測試方法捕獲,即fail(...)斷言觸發。

我究竟做錯了什么?

提前謝謝了!

用你的原始定義

public CompletionStage<Void> leaveGame(GameService gameService)
{
  return gameService.deregister(playerName)
                    .exceptionally(t -> {
                      LOGGER.info("Could not deregister: {}", t.getMessage());
                      throw new CompletionException(t);
                    });
}

方法leaveGame沒有拋出異常但總是返回一個未來。 調用者必須檢查未來以找出封裝的操作是否失敗。

同樣,當您將相同的代碼移動到Runnable

public CompletionStage<Void> leaveGame(GameService gameService)
{
    return CompletableFuture.runAsync(() -> System.out.println("BLAH"))
        .thenRun(() -> gameService.deregister(playerName)
                                  .exceptionally(t -> {
                                    LOGGER.info("Could not deregister: {}", t.getMessage());
                                    throw new CompletionException(t);
                                  }));
}

Runnable不會拋出異常。 仍然需要檢查gameService.deregister(…).exceptionally(…)返回的gameService.deregister(…).exceptionally(…)以確定它是否失敗,但現在,您不是返回它而是刪除引用。

要創建其完成取決於函數評估返回的未來的未來,您需要thenCompose

public CompletionStage<Void> leaveGame(GameService gameService)
{
    return CompletableFuture.runAsync(() -> System.out.println("BLAH"))
        .thenCompose(voidArg -> gameService.deregister(playerName)
                                  .exceptionally(t -> {
                                    LOGGER.info("Could not deregister: {}", t.getMessage());
                                    throw new CompletionException(t);
                                  }));
}

所以現在你正在實現一個Function<Void,CompletionStage<Void>>而不是Runnable並且函數返回的階段將用於完成leaveGame返回的未來。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM