使用CompletableFuture适应异常的正确方法 [英] Proper way to adapt an exception with CompletableFuture

查看:343
本文介绍了使用CompletableFuture适应异常的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在链接CompletableFuture来适应例外。虽然我有一些工作,我不明白为什么它的工作。

I am working on chaining CompletableFuture to adapt an exception. While I have something that is working, I don't understand why it works.

@Test
public void futureExceptionAdapt() throws ExecutionException, InterruptedException {
    class SillyException extends Exception { }
    class AdaptedException extends Exception { AdaptedException(SillyException silly) { } }

    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        sleepForThreeSeconds();
        if (true)
            throw new CompletionException(new SillyException());
        return 5;
    })

    .thenApplyAsync(val -> val * 10);

    CompletableFuture<Integer> futureAdaptException = future.exceptionally((t) -> {
        if (t instanceof CompletionException && t.getCause() instanceof SillyException) {
            System.out.println("adapt SillyException to AdaptedException");
            SillyException silly = (SillyException) t.getCause();
            future.obtrudeException(new AdaptedException(silly));
        }
        return null;
    });

    try {
        future.get();
        fail("future should have failed with an exception");
    } catch (ExecutionException e) {
        assertTrue("got exception: " + getCauseClass(e),
                   e.getCause() instanceof AdaptedException);
    }

    // I am not sure why the above succeeds
    // because I did not call futureAdaptException.get()
    // According to the IDE futureAdaptException is an unused variable at this point

    assertTrue("expect futureAdaptException to have failed with AdaptedException but instead the result is: " + futureAdaptException.get(),
               futureAdaptException.isCompletedExceptionally());
}

private static void sleepForThreeSeconds() {
    try {
        Thread.sleep(3000L);
    } catch (InterruptedException e) {
    }
}

private static String getCauseClass(Throwable t) {
    if (t.getCause() == null) return "null";
    return t.getCause().getClass().getName();
}

第一个问题是为什么 futureAdaptException 调用 future.get()

First question is why is futureAdaptException called when I simply call future.get()?

其次,是否有任何方式获取 futureAdaptException.get()将失败并出现所需的异常?因为我不想创建一个虚拟对象,如果我不必。您不能从lambda修改 futureAdaptException

Secondly, is there any way to get futureAdaptException.get() to fail with the desired exception? Because I don't want to create a dummy object if I don't have to. You cannot modify futureAdaptException from the lambda.

或者也许有更好的方式来适应异常。或者也许有一个变种的异常(),保持对象处于异常阶段(似乎返回null 原因 futureAdaptException 将对象放入正常阶段,值为 null )。或者也许我们不应该在 CompletableFuture 中调整例外。

Or maybe there is a better way to adapt exceptions. Or maybe there is a variant of exceptionally() that keeps the object in an exception stage (seems the return null causes futureAdaptException to put the object into normal stage with value null). Or maybe we shouldn't be adapting exceptions in CompletableFutures.

    @Test
public void futureStrangeBehaviorFromChaining1() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        System.out.println("sleep for 3 seconds then return 5");
        sleepForThreeSeconds();
        return 5;
    });

    future.thenApplyAsync(val -> {
        System.out.println("multiply by 3");
        return val*3;
    });

    sleepForFiveSeconds();

    assertEquals(5, future.get().intValue());

    sleepForFiveSeconds();

    assertEquals(5, future.get().intValue());
}

@Test
public void futureStrangeBehaviorFromChaining2() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        System.out.println("sleep for 3 seconds then return 5");
        sleepForThreeSeconds();
        return 5;
    })

    .thenApplyAsync(val -> {
        System.out.println("multiply by 3");
        return val*3;
    });

    sleepForFiveSeconds();

    assertEquals(15, future.get().intValue());

    sleepForFiveSeconds();

    assertEquals(15, future.get().intValue());
}


推荐答案

当您调用 future.get()时,futureAdaptException 不是调用。会发生什么事情是用 future.exceptionally()创建的,所以当将来完成异常时,它会自动触发。

futureAdaptException is not "called" when you call future.get(). What happens is that you created it with future.exceptionally() so it will automatically be triggered when future completes exceptionally.

所以即使 futureAdaptException 未使用(所以你可以删除变量), ()仍然有一个副作用。

So even though futureAdaptException is unused (so you could remove the variable), exceptionally() still has a side effect.

您获得的 CompletableFuture code> except()将成功或失败,具体取决于您在传入功能中的功能。如果你想要失败,你仍然可以再次抛出一个异常:

The CompletableFuture that you get from exceptionally() will succeed or fail depending on what you do in the passed-in function. If you want it to fail, you can still throw an exception again:

CompletableFuture<Integer> futureAdaptException = future.exceptionally((t) -> {
    if (t instanceof CompletionException && t.getCause() instanceof SillyException) {
        System.out.println("adapt SillyException to AdaptedException");
        SillyException silly = (SillyException) t.getCause();
        final AdaptedException ex = new AdaptedException(silly);
        future.obtrudeException(ex);
        throw new CompletionException(ex);
    }
    return null;
});

请注意,您应该避免使用 obtrudeException()因为这是非确定性的。如果事实上,我感到惊讶的是,你的第一个断言是成功的。由 except()返回的 CompletableFuture 将以与原始结果相同的结果完成,如果成功,则应该工作与此相反。

Note that you should probably avoid using obtrudeException() as this is non-deterministic. If fact, I am surprised that your first assert succeeds¹. The CompletableFuture returned by exceptionally() would complete with the same result as the original if it succeeds, so you should work with that one instead.

¹我绝对认为这是由于JDK中的一个错误。如果您在异常()中添加了一个 sleepForThreeSeconds(),则测试仍然通过。但是,如果在 future.get()之前添加了3秒以上的睡眠,则断言失败,您将获得原始异常。如果在完成之前调用 get(),它似乎等待异常()也执行。我已经发布了这个问题了解更好。

¹ I definitely think this is due to a bug in the JDK. If you add a sleepForThreeSeconds() in exceptionally(), the test still passes. However, if you add a sleep for more than 3s before future.get() the assert fails and you get the original exception. If you call get() before completion, it appears to wait for the exceptionally() to execute as well. I have posted this question to understand this better.

这篇关于使用CompletableFuture适应异常的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆