如何在java中实现改变输出类型的promise

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

我正在尝试用java实现一个简单的promise系统。我这样做是为了特殊目的,所以请不要推荐任何库。

当我尝试实现一个以 Function 作为参数的

thenApply()
方法时遇到问题,类似于 CompletableFuture 所具有的方法,因此返回另一种类型的承诺。

承诺界面:

public interface Promise<T> {
    Promise<T> then(Consumer<T> handler);

    <U> Promise<U> thenApply(Function<T, U> handler);
}

到目前为止我的实施:

public class PromiseImpl<T> implements Promise<T> {

    private List<Consumer<T>> resultHandlers = new ArrayList<>();

    public PromiseImpl(CompletableFuture<T> future) {
        future.thenAccept(this::doWork);
    }

    @Override
    public Promise<T> then(Consumer<T> handler) {
        resultHandlers.add(handler);
        return this;
    }

    @Override
    public <U> Promise<U> thenApply(Function<T, U> handler) {
        // How to implement here??? I don't have the result yet
        handler.apply(?);
    }

    private void onResult(T result) {
        for (Consumer<T> handler : resultHandlers) {
            handler.accept(result);
        }
    }

    private Object doWork(T result) {
        onResult(result);
        return null;
    }
}

问题是我不知道

thenApply()
方法中初始 future 的结果,所以我无法调用我的处理程序。另外,我不想调用
future.get()
,因为这个方法是阻塞的。

我怎样才能做到这一点?

java promise
4个回答
3
投票

真正的问题在于你的

Promise
类型的设计。它持有一组回调,所有这些回调都将在完成时调用。这是一个基本问题(限制
thenApply
函数的返回类型的通用功能)。这可以通过更改您的
Promise
实现来解决,以便在注册处理程序时返回
new
Promise,而不是返回
this
,这样每个 Promise 对象都会有自己的处理程序要调用。

除了解决这个问题之外,它还是函数式编程的更好设计,因为您可以使

Promise
对象不可变。

我会将界面更改为:

interface Promise<T> {
    <U> Promise<U> thenApply(Function<T, U> handler);
    Promise<Void> thenAccept(Consumer<T> consumer);
}

回调的“链接”可以围绕链接的

Promise
实例所引用的未来对象来完成。所以实现可以是这样的:

class PromiseImpl<T> implements Promise<T> {

    private CompletableFuture<T> future;

    public PromiseImpl(CompletableFuture<T> future) {
        this.future = future;
    }

    @Override
    public <U> Promise<U> thenApply(Function<T, U> function) {
        return new PromiseImpl<>(this.future.thenApply(function));
    }

    @Override
    public Promise<Void> thenAccept(Consumer<T> consumer) {
        return new PromiseImpl<>(this.future.thenAccept(consumer));
    }

    private void onResult(T result) {
        this.future.complete(result);
    }

    private Object doWork(T result) {
        onResult(result);
        return null;
    }
}

使用它可以很简单:

Promise<String> stringPromise = new PromiseImpl<>(new CompletableFuture<String>());
Promise<Long> longPromise = stringPromise.thenApply(str -> Long.valueOf(str.length()));
Promise<Void> voidPromise = stringPromise.thenAccept(str -> System.out.println(str));

编辑:
关于 Michael 的关于检索值的评论:未添加该评论,因为它不在原始

Promise
API 中。但添加起来很容易:

T get(); //To the interface

并实施:

public T get() {
    //try-catch 
    return this.future.get();
}

注意:这开始看起来越来越像

CompletableFuture
的重复,这提出了为什么要这样做的问题。但假设此接口中还会有其他类似
Promise
的方法,该方法将包装未来的 API。


如果您需要使用相同的

Promise
对象和回调列表,那么您别无选择,只能使用两个
Promise
具体类型参数来参数化
Function
接口:

public interface Promise<T, U>

并且

U
不能作为
then
thenApply
上的方法泛型参数。


2
投票

如果你想保持类的其余部分相同并只实现

thenApply
方法,则必须创建一个新的
CompletableFuture
,因为这是当前构建新
Promise
的唯一方法:

@Override
public <U> Promise<U> thenApply(Function<T, U> handler) {
    CompletableFuture<U> downstream = new CompletableFuture<>();
    this.then(t -> downstream.complete(handler.apply(t)));
    return new PromiseImpl<>(downstream);
}

如果您可以为

PromiseImpl
添加私有无参构造函数,则可以避免创建新的
CompletableFuture
:

@Override
public <U> Promise<U> thenApply(Function<T, U> handler) {
    PromiseImpl result = new PromiseImpl();
    this.then(t -> result.doWork(handler.apply(t)));
    return result;
}

但如果你想在

CompletableFuture
之上实现自己的 API,你真正应该做的是使用装饰器模式并将
CompletableFuture
实例包装为
PromiseImpl
中的私有变量。


0
投票

您可以返回一些扩展 PromiseImpl 并覆盖 onResult 的匿名类,以便处理程序接受应用映射器函数的结果。不要忘记调用父级 onResult,以便调用父级处理程序。


0
投票

我为 java 和 kotlin 编写了一个 Promise 库。它更易于使用且轻巧,请在 https://pro4j.com/ 处查看。

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