[英]How to handle uncaught exceptions from 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.