我正在尝试在Java中实现一个简单的Promise系统。我这样做是出于特殊目的,所以请不要推荐任何库。
当我尝试实现将函数作为参数的thenApply()
方法时,遇到了一个问题,类似于CompletableFuture的方法,因此返回了另一个类型的promise。
promise接口:
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.get()
,因为此方法正在阻止。
我该如何做?
答案 0 :(得分:3)
真正的问题是您的Promise
类型的设计。它拥有一组回调,所有回调都将在完成时被调用。这是一个基本问题(将通用功能限制在thenApply
函数的返回类型周围)。这可以通过更改您的Promise
实现来解决,只要在注册处理程序时就返回new
承诺,而不是返回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's comment检索值:未添加,因为它不是原始Promise
API中的值。但这很容易添加:
T get(); //To the interface
并通过以下方式实现:
public T get() {
//try-catch
return this.future.get();
}
注意:这看起来越来越像是CompletableFuture
的副本,这引出了为什么要这么做的问题。但是假设此接口中将有其他类似Promise
的方法,则该方法将包装将来的API。
如果您需要使用相同的Promise
对象和回调列表,那么别无选择,只能使用两个Promise
具体类型参数对Function
接口进行参数化:< / p>
public interface Promise<T, U>
并且U
不能成为then
或thenApply
上的方法通用参数。
答案 1 :(得分:2)
如果您想保持班级的其余部分不变,并且只实现thenApply
方法,则必须创建一个新的CompletableFuture
,因为这是当前您必须构造新的{ {1}}:
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;
}
上实现自己的API,实际上应该使用装饰器模式并将CompletableFuture
实例包装为CompletableFuture
中的私有变量。
答案 2 :(得分:0)
您可以返回一些扩展PromiseImpl并覆盖onResult的匿名类,以便处理程序接受应用mapper函数的结果。不要忘记调用父onResult,这样就会调用父处理程序。