[英]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.