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(...)
completeExceptionally 和 return 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);
}));
}
异常分支is仍然触发,但异常现在没有被测试方法捕获,即fail(...)
断言is 触发。
我做错了什么?
非常感谢!
用你原来的定义
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
like
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(…)
返回的 future 以确定它是否失败,但现在,您不是返回它,而是删除引用。
要创建一个未来,其完成取决于函数评估返回的未来,您需要 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
.[=22= 返回的未来]
早上好,
我不太了解 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(...)
completeExceptionally 和 return 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);
}));
}
异常分支is仍然触发,但异常现在没有被测试方法捕获,即fail(...)
断言is 触发。
我做错了什么?
非常感谢!
用你原来的定义
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
like
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(…)
返回的 future 以确定它是否失败,但现在,您不是返回它,而是删除引用。
要创建一个未来,其完成取决于函数评估返回的未来,您需要 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
.[=22= 返回的未来]