CompletableFuture.runAsync吞咽异常

问题描述 投票:0回答:1

早上好,

我不太了解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

在上述情况下,如预期的那样,异常分支被触发,消息被记录,并且捕获了单元测试中的异常,即,未触发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被触发。

我在做什么错?

非常感谢!

java exception completable-future
1个回答
0
投票

使用您的原始定义

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(…)返回的未来,以了解是否失败,但是现在,您不是退还而是删除参考。

要创建一个未来,其完成取决于功能评估返回的未来,您需要thenCompose

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返回的未来。

© www.soinside.com 2019 - 2024. All rights reserved.