java 如何组合 3 个或更多 CompletionStages?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34004802/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 22:24:04  来源:igfitidea点击:

How to combine 3 or more CompletionStages?

javaconcurrencyjava-8

提问by Vladimir Korenev

If have 2 CompletionStages I can combine them with thenCombinemethod:

如果有 2 CompletionStages,我可以将它们与thenCombine方法结合起来:

CompletionStage<A> aCompletionStage = getA();
CompletionStage<B> bCompletionStage = getB();
CompletionStage<Combined> combinedCompletionStage =
    aCompletionStage.thenCombine(bCompletionStage, (aData, bData) -> combine(aData, bData));

If I have 3 or more CompletionStages I can make a chain of thenCombinemethods, but I have to use temporary objects to pass results. For example, here is a solution using Pairand Triplefrom the org.apache.commons.lang3.tuplepackage:

如果我有 3 个或更多 CompletionStages,我可以创建一系列thenCombine方法,但我必须使用临时对象来传递结果。例如,这是一个使用PairTriple来自org.apache.commons.lang3.tuple包的解决方案:

CompletionStage<A> aCompletionStage = getA();
CompletionStage<B> bCompletionStage = getB();
CompletionStage<C> cCompletionStage = getC();
CompletionStage<D> dCompletionStage = getD();

CompletionStage<Combined> combinedDataCompletionStage =
        aCompletionStage.thenCombine(bCompletionStage, (Pair::of))
                .thenCombine(cCompletionStage, (ab, c) ->
                        Triple.of(ab.getLeft(), ab.getRight(), c))
                .thenCombine(dCompletionStage, (abc, d) ->
                        combine(abc.getLeft(), abc.getMiddle(), abc.getRight(), d));

Is there a better way to combine results from multiple CompletionStages?

有没有更好的方法来组合多个 CompletionStages 的结果?

回答by Holger

The only way to combine multiple stages that scales well with a growing number of stages, is to use CompletableFuture. If your CompletionStages aren't CompletableFutures you may still convert them using .toCompletableFuture():

将多个阶段与越来越多的阶段结合起来的唯一方法是使用CompletableFuture. 如果您的CompletionStages 不是CompletableFutures 您仍然可以使用.toCompletableFuture()以下方法转换它们:

CompletableFuture<A> aCompletionStage = getA().toCompletableFuture();
CompletableFuture<B> bCompletionStage = getB().toCompletableFuture();
CompletableFuture<C> cCompletionStage = getC().toCompletableFuture();
CompletableFuture<D> dCompletionStage = getD().toCompletableFuture();

CompletionStage<Combined> combinedDataCompletionStage = CompletableFuture.allOf(
    aCompletionStage, bCompletionStage, cCompletionStage, dCompletionStage)
    .thenApply(ignoredVoid -> combine(
        aCompletionStage.join(), bCompletionStage.join(),
        cCompletionStage.join(), dCompletionStage.join()) );

This contains more boilerplate than combining two stages via thenCombinebut the boilerplate doesn't grow when adding more stages to it.

这比合并两个阶段包含更多的样板,thenCombine但在向其中添加更多阶段时样板不会增长。



Note that even with your original thenCombineapproach, you don't need a Triple, a Pairis sufficient:

请注意,即使使用原始thenCombine方法,您也不需要 a Triple, aPair就足够了:

CompletionStage<Combined> combinedDataCompletionStage =
    aCompletionStage.thenCombine(bCompletionStage, (Pair::of)).thenCombine(
        cCompletionStage.thenCombine(dCompletionStage, Pair::of),
        (ab, cd) -> combine(ab.getLeft(), ab.getRight(), cd.getLeft(), cd.getRight()));

Still, it doesn't scale well if you want to combine more stages.

尽管如此,如果您想组合更多阶段,它的扩展性并不好。



An in-between solution (regarding complexity) might be:

中间解决方案(关于复杂性)可能是:

CompletionStage<Combined> combinedDataCompletionStage = aCompletionStage.thenCompose(
    a -> bCompletionStage.thenCompose(b -> cCompletionStage.thenCompose(
        c -> dCompletionStage.thenApply(d -> combine(a, b, c, d)))));

That's simpler in its structure but still doesn't scale well with more more stages.

它的结构更简单,但仍然不能很好地扩展更多阶段。

回答by Vladimir Korenev

Holger's third answercan be made a little bit shorter:

霍尔格的第三个答案可以缩短一点:

CompletionStage<Combined> combinedDataCompletionStage = aCompletionStage.thenCompose(
    a -> bCompletionStage.thenCompose(
        b -> cCompletionStage.thenCombine(dCompletionStage,
            (c, d) -> combine(a, b, c, d))));

回答by Itchy

You asked about "3 or more", if you have them in a List as CompletableFutures (see other answers) you could use this handy method:

您询问了“3 个或更多”,如果您将它们作为 CompletableFutures 放在列表中(请参阅其他答案),您可以使用这个方便的方法:

private static <T> CompletableFuture<List<T>> join(List<CompletableFuture<T>> executionPromises) {
    CompletableFuture<Void> joinedPromise = CompletableFuture.allOf(executionPromises.toArray(CompletableFuture[]::new));
    return joinedPromise.thenApply(voit -> executionPromises.stream().map(CompletableFuture::join).collect(Collectors.toList()));
}

It converts your "list of futures" to a "future for a list of the results".

它将您的“期货清单”转换为“结果清单的未来”。

回答by Caspar Nonclercq

I think you should use an intermediary object, but one of your own instead of using Pairand Tuple

我认为你应该使用一个中间对象,但是你自己的一个而不是使用PairTuple

public R method() {
    CompletableFuture<A> aFuture = getAFuture();
    CompletableFuture<B> bFuture = getBFuture();
    CompletableFuture<C> cFuture = getCFuture();
    CompletableFuture<D> dFuture = getDFuture();

    return CompletableFuture.completedFuture(new WellNamedResultHolder())
            .thenCombineAsync(aFuture, WellNamedResultHolder::withAResult)
            .thenCombineAsync(bFuture, WellNamedResultHolder::withBResult)
            .thenCombineAsync(cFuture, WellNamedResultHolder::withCResult)
            .thenCombineAsync(dFuture, WellNamedResultHolder::withDResult)
            .thenApplyAsync(this::combineAllTheResults);
}

private static class WellNamedResultHolder {
    private A aResult;
    private B bResult;
    private C cResult;
    private D dResult;

    // Getters

    public WellNamedResultHolder withAResult(final A aResult) {
        this.aResult = aResult;
        return this;
    }

    public WellNamedResultHolder withBResult(final B bResult) {
        this.bResult = bResult;
        return this;
    }

    public WellNamedResultHolder withCResult(final C cResult) {
        this.cResult = cResult;
        return this;
    }

    public WellNamedResultHolder withDResult(final D dResult) {
        this.dResult = dResult;
        return this;
    }
}

The actual form of the result holder can obviously change to match your own needs, giving you greater flexibility. You also get to be in charge of what happens as these futures complete. Although there is more boilerplate, you get code that is more descriptive of what is happening (which lombok can tidy up).

结果持有者的实际形式显然可以更改以满足您自己的需求,从而为您提供更大的灵活性。您还可以负责这些期货完成后会发生什么。尽管有更多样板,但您会得到更能描述正在发生的事情的代码(lombok 可以整理这些代码)。

回答by Tat Huan

I think that CompleableFuture.allOf() function can help you.

我认为 CompleableFuture.allOf() 函数可以帮助你。

For example: (View full Class here)

例如:(在此处查看完整课程)

    List<String> urls = [
            "https://webhook.site/1647465b-c28f-4ffe-bbfe-5d3ad95ef994",
            "https://webhook.site/1647465b-c28f-4ffe-bbfe-5d3ad95ef994?a=1"
    ]
    CompletableFuture<Response>[] futures = new Completablefuture[2]
    for (int i = 0; i < urls.size(); i++) {
        futures[i] = asyncHttpClient.prepareGet(urls[i]).execute().toCompletableFuture()

    }

    CompletableFuture.allOf(futures).thenApply { future ->
        return futures.collect { it.join() }
    }.thenApply({ responses ->
        //Do something with results
        responses.each { println("Status code: " + it.statusCode) }
    })

回答by TriCore

Any number of CompletableFuture can be combined (reduced)

可以组合任意数量的 CompletableFuture(减少)

CompletionStage<A> futA = getA();
CompletionStage<B> futB = getB(); 
CompletionStage<C> futC = getC(); 

Stream.of(futA, futB, futC) 
    .reduce((f1, f2) -> f1.thenCombine(f2, (d1, d2) -> combine(d1, d2));

The implementation of combinemethod will be responsible to merge data values (A, B and C), which could be tricky if A, B and C are disparate.

combine方法的实现将负责合并数据值(A、B 和 C),如果 A、B 和 C 不同,这可能会很棘手。

回答by Krish

I had a similar problem but had more than 3 completablefutures so building up on Holger'sanswer I made a small generic utility.

我有一个类似的问题,但有超过 3 个可完成的未来,所以根据Holger 的回答,我做了一个小的通用实用程序。

public static <T, R> CompletableFuture<R> allOf(List<CompletableFuture<T>> args, Function<List<T>, R> combiner) {
    final Queue<CompletableFuture<T>> queue = new LinkedList<>();
    for (CompletableFuture<T> arg : args) {
        queue.add(arg);
    }
    return aggregator(queue, new ArrayList<>(), combiner);
}

private static <T, R> CompletableFuture<R> aggregator(Queue<CompletableFuture<T>> queue, List<T> arg,
        Function<List<T>, R> combiner) {
    if (queue.size() == 2)
        return queue.poll().thenCombine(queue.poll(), (c, d) -> {
            arg.add(c);
            arg.add(d);
            return combiner.apply(arg);
        });
    return queue.poll().thenCompose(data -> {
        arg.add(data);
        return aggregator(queue, arg, combiner);
    });
}