【问题标题】:Combining CompletableFutures into a tree-like Structure将 CompletableFutures 组合成树状结构
【发布时间】:2014-07-28 09:48:21
【问题描述】:

我目前正在试验 Java 8 的 Future API 和 Core CompleteableFuture。我知道它是一个 monad,所以它应该有一个 bind-Operator。

现在的想法是我有一个抽象操作 k 的树,例如像这样:

O --- > O3 ---> O4 ---> o5
  O ---> o2 --/

我想将O3O2“合并”成一个新的操作O4 = (O3 o O2) (t) = O2(O3(t))。 我的想法是我是例如在O2 中并想说:将当前节点与O3 合并,并返回一个由串联操作组成的新节点。 不幸的是,我已经尝试了一整晚,但我无法弄清楚。 此外,由于未知原因,使用 O1.mergeWith(O2).mergeWith(O3) 之类的运算符会触发 apply 方法两次以进行单次调用。

目标是创建一个由其他函数组成的新函数,所以我可以尽可能推迟计算。

public abstract class Operation<T,R> {
  // The value a
  private final CompletableFuture<R> future;

// Executes the operation on t and returns something (maybe different) of type R
  protected abstract R apply(T t);

// Gets the value of the future of this operation
  public R get() throws Exception {
    return future.get();
  }

  protected Operation(Supplier<T> s) {
    future =  CompletableFuture.supplyAsync(s).thenApplyAsync(transform());
  }

  protected Operation(CompletableFuture<R> f) {
    future = f;
  }


  CompletableFuture<R> createTargetFuture(R _value) {
    return CompletableFuture.supplyAsync(() -> _value);
  }

  <S> Operation<T,S> mergeWith(Operation<R,S> _other) {
    CompletableFuture<S> _result = future.thenComposeAsync(
      // Method is synchronous, but is run async :) We need the createTargetFuture,         otherwise we cannot turn the constant t into a producer.
      (t) -> _other.createTargetFuture(_other.apply(t)));
      //transform()).thenApplyAsync(_other.transform());
    return new Operation<T, S>(_result) {
      @Override
      protected S apply(T t) {
        // What to put here
        return null;
      }
    };
  }

  protected Function<T,R> transform(){
    return this::apply;
  }
}

【问题讨论】:

    标签: java functional-programming monads java-8


    【解决方案1】:

    目前还不清楚您要实现的目标。您的整个 Operation 类看起来就像您正在向 CompletableFuture 添加一个功能,它已经提供了它自己的功能。

    如果您有一个 Supplier&lt;T&gt; s 并想推迟它的执行,您可以按照您已经知道的方式执行 CompletableFuture.supplyAsync(s)

    现在,如果您想将Function&lt;T,R&gt; f 应用于CompletableFuture&lt;T&gt; a 的延迟结果,只需使用CompletableFuture.supplyAsync(()-&gt;f.apply(a.join()))

    同样适用于两个CompletableFutures 和一个BiFunctionCompletableFuture.supplyAsync(()-&gt;f.apply(a.join(),b.join()))

    如果您觉得需要将操作封装在某种支持代码中,您可以创建如下实用方法:

    public static <T> CompletableFuture<T> create(Supplier<T> s) {
        return CompletableFuture.supplyAsync(s);
    }
    public static <T,R> CompletableFuture<R> create(
      Function<T,R> f, CompletableFuture<T> a) {
        return CompletableFuture.supplyAsync(()->f.apply(a.join()));
    }
    public static <T,U,R> CompletableFuture<R> create(
      BiFunction<T,U,R> f, CompletableFuture<T> a, CompletableFuture<U> b) {
        return CompletableFuture.supplyAsync(()->f.apply(a.join(),b.join()));
    }
    

    然后你就可以愉快地执行异步操作了:

    int i=create((a,b)->a+b, create(()->42),create(()->100)).join();
    

    但是,当然,同样的事情也可以在没有任何支持方法的情况下工作:

    int i=CompletableFuture.supplyAsync(() ->
        CompletableFuture.supplyAsync(()->42).join()
      + CompletableFuture.supplyAsync(()->100).join() ).join();
    

    int j=CompletableFuture.supplyAsync(()->42).thenApplyAsync(
        a-> a + CompletableFuture.supplyAsync(()->100).join() ).join();
    

    【讨论】:

    • 一个操作是一个抽象类,子类可以实现诸如“获取图像,调整大小并返回调整大小的图像”之类的东西。所以最终会有更多的功能,而不仅仅是 CompleteableFuture 部分。
    • 您没有解释为什么需要在Operation 的子类而不是简单的Function&lt;T,R&gt; 中实现您描述的操作。目前,您有超过 40 行代码对 CompletableFuture 没有任何好处,但会引入错误。也许你应该专注于“更多功能”:只需删除错误代码并创建一个 simple 包装器。如果您让它实现Supplier,您可以使用我的答案的代码片段而无需任何更改......
    • 感谢您的努力 :) 我重新考虑了一切,您提供了一些很好的起点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-09
    • 1970-01-01
    • 2020-02-27
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    相关资源
    最近更新 更多