未来与承诺之间有什么区别?


274

Future和之间有什么区别Promise
它们都充当未来结果的占位符,但是主要区别在哪里?


99
您可以制作一个,Promise并且要保留它。当别人向您许下诺言时,您必须等待,看看他们是否兑现了诺言Future
凯文·赖特


30
我读过的最没有帮助的Wikipedia文章之一
Fulluphigh

Answers:


145

根据讨论Promise终于被要求CompletableFuture包含在Java 8中,其javadoc解释了:

可以明确完成(设置其值和状态)并可以用作CompletionStage的Future,它支持在完成时触发的相关功能和操作。

清单上还提供了一个示例:

f.then((s -> aStringFunction(s)).thenAsync(s -> ...);

请注意,最终的API稍有不同,但允许类似的异步执行:

CompletableFuture<String> f = ...;
f.thenApply(this::modifyString).thenAccept(System.out::println);

78
这不是您的错,Assylias,但是Javadoc提取需要由体面的技术作者进行认真的检查。在我的第五通读本中,我可以开始欣赏它要说的内容了……而我在理解了已经存在的期货和承诺的情况下来到了这里!
甜菜根-甜菜根

2
@ Beetroot-Beetroot看来现在已经发生了。
Herman 2015年

1
@herman谢谢-我已经更新了链接,以指向最终版本的javadoc。
assylias 2015年

7
@ Beetroot-Beetroot您应该签出Exceptionally方法的文档。这将是一首美妙的诗,但是却是可读文档的一个例外。
Fulluphigh

4
对于任何想知道的人,@Fulluphigh都是指this。看起来好像是删除/大修在Java中8
塞德里克·赖兴巴赫

147

(到目前为止,我对答案并不完全满意,所以这是我的尝试...)

我认为,凯文·赖特(Kevin Wright)的评论(“您可以作出承诺,而您应遵守承诺。当其他人向您许下诺言时,您必须拭目以待,看看他们是否会在未来兑现它”),但它的总结很不错,但是有些解释会很有用。

期货和承诺是非常相似的概念,不同之处在于,期货是一个尚不存在的结果的只读容器,而承诺可以被写入(通常只能写入一次)。可以将Java 8 CompletableFuture和Guava SettableFuture视为承诺,因为可以设置它们的值(“ completed”),但是它们也实现Future接口,因此客户端没有区别。

未来的结果将由“其他人”设置-由异步计算的结果确定。请注意,必须使用Callable或Runnable初始化FutureTask(经典的Future),没有无参数构造函数,并且Future和FutureTask都是从外部只读的(FutureTask的set方法受保护)。该值将从内部设置为计算结果。

另一方面,承诺的结果可以由“您”(或实际上任何人)随时设置,因为它具有公共设置方法。可以创建CompletableFuture和SettableFuture,而无需执行任何任务,并且可以随时设置它们的值。您向客户代码发送一个承诺,并在以后根据需要履行。

请注意,CompletableFuture并非“纯粹的”承诺,可以使用诸如FutureTask之类的任务对其进行初始化,并且其最有用的功能是与处理步骤无关的链接。

还要注意,promise不必是Future的子类型,也不必是同一对象。在Scala中,通过异步计算或其他 Promise对象创建Future 对象。在C ++中,情况类似:生产者使用Promise对象,消费者使用Future对象。这种分离的优点是客户无法设定未来的价值。

两个弹簧EJB 3.1具有AsyncResult类,它是类似于斯卡拉/ C ++承诺。AsyncResult确实实现了Future,但这不是真正的未来:Spring / EJB中的异步方法通过一些背景魔术返回一个不同的只读Future对象,并且客户端可以使用第二个“真实”的Future来访问结果。


116

我知道已经有了一个可以接受的答案,但还是要加上我的两分钱:

TLDR:Future和Promise是异步操作的两个方面:消费者/调用者生产者/实现者

作为异步API方法的调用者,您将获得a Future作为计算结果的句柄。您可以例如调用get()它以等待计算完成并检索结果。

现在考虑一下该API方法的实际实现方式:实现者必须Future立即返回一个。他们有责任在计算完成后立即完成将来的任务(由于执行分派逻辑,它们会知道这一点;-)。他们将使用Promise/ CompletableFuture来做到这一点:构造并CompletableFuture立即返回立即数,并complete(T result)在计算完成后立即调用。


1
这是否意味着Promise始终是Future的子类,并且Future的可写性只是被类型遮盖了?
devios1

我不认为这是隐含的。在实现方面,通常是这样(例如,在Java,Scala中)。
RahelLüthy18年

74

我将举一个什么是Promise以及如何在任何时候设置其值的示例,与Future相反,后者仅是可读的。

假设您有一个妈妈,您要她要钱。

// Now , you trick your mom into creating you a promise of eventual
// donation, she gives you that promise object, but she is not really
// in rush to fulfill it yet:
Supplier<Integer> momsPurse = ()-> {

        try {
            Thread.sleep(1000);//mom is busy
        } catch (InterruptedException e) {
            ;
        }

        return 100;

    };


ExecutorService ex = Executors.newFixedThreadPool(10);

CompletableFuture<Integer> promise =  
CompletableFuture.supplyAsync(momsPurse, ex);

// You are happy, you run to thank you your mom:
promise.thenAccept(u->System.out.println("Thank you mom for $" + u ));

// But your father interferes and generally aborts mom's plans and 
// completes the promise (sets its value!) with far lesser contribution,
// as fathers do, very resolutely, while mom is slowly opening her purse 
// (remember the Thread.sleep(...)) :
promise.complete(10); 

输出是:

Thank you mom for $10

妈妈的诺言已创建,但等待一些“完成”事件。

CompletableFuture<Integer> promise...

您创建了这样的活动,接受了她的诺言并宣布了要感谢您妈妈的计划:

promise.thenAccept...

这时妈妈开始打开钱包...但是非常慢...

父亲比您妈妈更快地干预并完成了诺言:

promise.complete(10);

您是否注意到我明确写过的遗嘱执行人?

有趣的是,如果您使用默认的隐式执行器(commonPool)且父亲不在家里,而只有妈妈带着“慢钱包”,则她的诺言只会兑现,前提是该程序的寿命超过了妈妈从中获得钱的需要钱包。

默认执行程序的行为类似于“守护程序”,并且不等待所有诺言得到兑现。我还没有找到关于这个事实的好描述...


8
读这本书真是太有趣了!我认为我不能忘记未来并承诺。
user1532146

2
这必须作为答案。就像读一个故事。感谢@Vladimir
Phillen '19

感谢@Vladimir
intvprep

9

不知道这是否可以作为答案,但是正如我看到别人对某人说的那样,这两个概念似乎都需要两个单独的抽象,因此其中一个(Future)只是另一个概念的只读视图()。Promise)...但实际上并不需要。

例如看一下如何在javascript中定义promise:

https://promisesaplus.com/

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

重点是使用then类似方法的可组合性:

asyncOp1()
.then(function(op1Result){
  // do something
  return asyncOp2();
})
.then(function(op2Result){
  // do something more
  return asyncOp3();
})
.then(function(op3Result){
  // do something even more
  return syncOp4(op3Result);
})
...
.then(function(result){
  console.log(result);
})
.catch(function(error){
  console.log(error);
})

这使得异步计算看起来像同步:

try {
  op1Result = syncOp1();
  // do something
  op1Result = syncOp2();
  // do something more
  op3Result = syncOp3();
  // do something even more
  syncOp4(op3Result);
  ...
  console.log(result);
} catch(error) {
  console.log(error);
}

这很酷。(不像async-await那样酷,但是async-await只是删除了样板.... then(function(result){....)。)

实际上,它们的抽象作为promise构造器非常好

new Promise( function(resolve, reject) { /* do it */ } );

允许您提供两个回调,可用于Promise成功完成或发生错误。这样,只有构造的代码Promise才能完成它,而接收已经构造的Promise对象的代码具有只读视图。

如果解决方法和拒绝方法是受保护的方法,则通过继承可以实现上述目的。


4
+1。这是对这个问题的正确答案。 CompletableFuture可能与a有一些相似之处,Promise仍然不是aPromise,因为它打算使用的方式不同:a Promise的结果是通过调用消耗的then(function),并且函数在生产者调用之后立即在生产者的上下文中执行resolve。甲Future的结果是通过调用消耗get这会导致消费者线程等待,直到生产者线程已产生的值,则在消费处理它。 Future本质上是多线程的,但是...
Periata Breatta

5
...完全可以只使用一个Promise线程(实际上,这是它们最初设计的确切环境:javascript应用程序通常只有一个线程,因此您无法在其中实现Future)。 Promise因此,它更加轻巧和高效Future,但是Future在更复杂的情况下以及需要使用Promises 难以安排的线程之间的协作的情况下会很有用。总结一下:Promise是推模型,而Future拉模型(参见Iterable vs Observable)
Periata Breatta 2016年

@PeriataBreatta即使在单线程环境中,也必须满足诺言(通常以不同线程的形式运行,例如XMLHttpRequest)。我不相信效率要求,您碰巧有一些数字吗?+++那是一个很好的解释。
maaartinus

1
@maaartinus-是的,某些事情必须履行承诺,但是可以(通过使用事实上的许多情况下)使用顶级循环来完成,该循环轮询外部状态的变化并解析与完成的动作有关的任何承诺。在效率方面,我没有具体的Promises确切数字,但是请注意,调用get一个未解决的问题Future将必然涉及2个线程上下文切换,至少在几年前可能需要大约50 us
Periata Breatta '16

@PeriataBreatta实际上,您的评论应该是公认的解决方案。我一直在寻找像您一样的解释(拉/推,单/多线程)。
托马斯·雅各布

5

对于客户端代码,Promise用于在结果可用时观察或附加回调,而Future用于等待结果然后继续。从理论上讲,与期货有关的任何事情都与承诺有关,但由于样式的不同,所得到的用于不同语言的承诺API使得链接更容易。


2

Future接口中没有set方法,只有get方法,因此它是只读的。关于CompletableFuture,本文可能会有所帮助。 未来的完成

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.