我正在尝试用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()
,因为这个方法是阻塞的。
我怎样才能做到这一点?
真正的问题在于你的
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
上的方法泛型参数。
如果你想保持类的其余部分相同并只实现
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
中的私有变量。
您可以返回一些扩展 PromiseImpl 并覆盖 onResult 的匿名类,以便处理程序接受应用映射器函数的结果。不要忘记调用父级 onResult,以便调用父级处理程序。
我为 java 和 kotlin 编写了一个 Promise 库。它更易于使用且轻巧,请在 https://pro4j.com/ 处查看。