函数中的抛出异常适用于CompletableFutures

时间:2018-11-14 19:14:35

标签: java completable-future

我正在创建一些任务,如下所示(这只是为了演示正常的网络调用):

public class RandomTask implements Function<String, String> {
    private int number;
    private int waitTime;
    private boolean throwError;

    public RandomTask(int number, int waitTime, boolean throwError) {
        this.number = number;
        this.waitTime = waitTime;
        this.throwError = throwError;
    }

    @Override
    public String apply(String s) {
        System.out.println("Job " + number + " started");
        try {
            Thread.sleep(waitTime);

            if (throwError) {
                throw new InterruptedException("Something happened");
            }

        } catch (InterruptedException e) {
            System.out.println("Error " + e.getLocalizedMessage());
        }

        return "RandomTask " + number + " finished";
    }
}

然后我有一个Chain类,其中每个作业将一些任务链接在一起。

static CompletableFuture<String> start(ExecutorService executorService) {
    CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Foo", executorService)
            .thenApplyAsync(new RandomTask(3, 100, false), executorService)
            .thenApplyAsync(new RandomTask(4, 100, false), executorService);

    return future2;
}

然后我按如下所示开始两条链:

  CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(Chain1.start(fixedThreadPool), Chain2.start(fixedThreadPool));
    try {
        combinedFuture.get();
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }

这样,两条链就同时开始了。

现在,我想在任务中引发异常,并在调用CombinedFuture.get()的地方捕获该异常,以便我知道链中哪个任务失败了。

问题是,我无法适应Function,因为CompletableFutures对此有所抱怨。我尝试过:

@FunctionalInterface
public interface CheckedFunction<T, R> {
    R apply(T t) throws InterruptedException;
}

但这不起作用。这是不可能的,还是我如何实现我的目标?

1 个答案:

答案 0 :(得分:1)

“这样,两条链就同时开始了。”表明您对CompletableFuture的工作原理有根本的错误认识。

异步操作在创建时或在其先决条件可用时立即提交给执行者服务。因此,对于supplyAsync没有依赖性的情况,异步操作直接在supplyAsync调用内开始。

所有类似CompletableFuture.allOf(job1, job2).get()的构造,都是根据两个作业创建一个新阶段并等待其完成,因此最终结果只是等待两个作业的完成。它不会启动作业。他们已经在运行。等待完成不会影响完成过程。

使用允许检查异常的自定义函数类型绑定CompletableFuture可以

public static <T,R> CompletableFuture<R> thenApplyAsync(
    CompletableFuture<T> f, CheckedFunction<? super T, ? extends R> cf,
    Executor e) {

    CompletableFuture<R> r = new CompletableFuture<>();
    f.whenCompleteAsync((v,t) -> {
        try {
            if(t != null) r.completeExceptionally(t);
            else r.complete(cf.apply(v));
        } catch(Throwable t2) {
            r.completeExceptionally(t2);
        }
    }, e);
    return r;
}

要使用此方法,您不必嵌套CompletableFuture上的调用,而必须嵌套它们。例如

static CompletableFuture<String> start(ExecutorService executorService) {
    CompletableFuture<String> future2 = 
        thenApplyAsync(thenApplyAsync(
            CompletableFuture.supplyAsync(() -> "Foo", executorService),
            new RandomTask(3, 100, false), executorService),
            new RandomTask(4, 100, false), executorService);

    return future2;
}

给予

public class RandomTask implements CheckedFunction<String, String> {
    private int number, waitTime;
    private boolean throwError;

    public RandomTask(int number, int waitTime, boolean throwError) {
        this.number = number;
        this.waitTime = waitTime;
        this.throwError = throwError;
    }

    @Override
    public String apply(String s) throws InterruptedException {
        System.out.println("Job " + number + " started");
        Thread.sleep(waitTime);
        if (throwError) {
            throw new InterruptedException("Something happened in "+number);
        }
        return "RandomTask " + number + " finished";
    }
}

您仍然可以创建两个任务,然后等待两个任务

CompletableFuture.allOf(Chain1.start(fixedThreadPool), Chain2.start(fixedThreadPool))
    .join();