Future与promise

在计算机科学中，future、promise、delay和deferred是指用于在某些并发编程语言中同步程序执行的构造。由于某些计算（或者网络请求）尚未结束，我们需要一个对象来代理这个未知的结果，于是就有了上述这些构造（future、promise等）。
promise一词由丹尼尔·福瑞得曼和David Wise在1976年提出，
Peter Hibbard称之为eventual 。
1977年Henry Baker和Carl Hewitt在一篇论文中介绍了一个类似的概念future。
术语future、promise、delay和deferred通常可以互换使用，而future与promise之间的使用差异，我们将在下面讨论。具体来说，当区分使用时，future是变量的只读占位符视图，而promise是可写的单赋值容器，用于设置future的值。  值得注意的是，无须指定可以设置其值的promise就可以定义future，并且不同的promise可以设置同一个future的值，尽管对于给定的future仅可以执行一次。在其他情况下，future和promise是一起创建的，并且相互关联：future是值，promise是设定值的函数——本质上是异步函数（promise）的返回值（future）。设置future的值的过程也称为resolve（解析）、fulfil（实现）或bind（绑定）它。

应用

future和promise起源于函数式编程和相关范例（如逻辑编程 ），目的是将值（future）与其计算方式（promise）分离，从而允许更灵活地进行计算，特别是通过并行化。后来，它在分布式计算中得到了应用，减少了通信往返的延迟。再后来，它变得更有用了，因为它允许以直接风格编写异步程序，而不是以连续传递风格的方式。

隐式与显式

对future的使用可能是隐式的（任何对future的使用都会自动获得它的值，它就像是普通的引用一样）或显式的（用户必须调用函数来获取值，例如Java中的java.util.concurrent.Future或java.util.concurrent.CompletableFuture的get方法）。获得一个显式的future的值可以称为stinging或forcing。显式future可以作为库来实现，而隐式future则通常作为语言的一部分来实现。
最初的Baker和Hewitt论文描述了隐式future，它们在角色模型和纯面向对象编程语言（如Smalltalk）中自然得到支持。Friedman和Wise的论文只描述了显式的future，可能反映了在老旧硬件上有效实施隐式future的困难。难点在于老旧硬件不能处理原始数据类型（如整数）的future。例如，add指令不会处理3 + future factorial(100000) 。在纯角色模型或面向对象语言中，这个问题可以通过发送future factorial(100000)消息+[3]，它要求future自己加3并返回结果。请注意，无论factorial(100000)何时完成计算，消息传递方法都可以工作，而且不需要任何sting或force。

promise流水线

在分布式系统中使用future可以显著地减少延迟。例如，future让promise流水线成为了可能， 就像在E语言和Joule语言中实现的那样，在Argus语言中这也被称为call-stream。
考虑一个涉及常规远程过程调用的表达式，例如：
 t3 := ( x.a() ).c( y.b() )
可以扩展为
 t1 := x.a();
 t2 := y.b();
 t3 := t1.c(t2);
每个语句需要发送一条消息，并在下一个语句可以继续之前收到一个答复。例如，假设x、y、t1和t2都位于同一台远程机器上。在这种情况下，在开始执行第三条语句之前，必须对该机器进行两次完整的网络往返。然后，第三条语句将引起另一个到同一个远程机器的往返。
使用future，上面的表达式可以写成
 t3 := (x <- a()) <- c(y <- b())
可以扩展为
 t1 := x <- a();
 t2 := y <- b();
 t3 := t1 <- c(t2);
这里使用的语法是E语言的语法，其中x <- a()表示将消息a()异步发送给x。所有三个变量都会立即为其结果分配future，执行过程将继续进行到后面的语句。之后尝试解决t3的值可能会导致延迟；但是，流水线操作可以减少所需的往返次数。如果与前面的示例一样，x、y、t1和t2都位于相同的远程机器上，则流水线实现可以用一次往返来计算t3，不必用三次。由于所有三条消息都指向同一远程计算机上的对象，因此只需要发送一个请求，只需要接收一个包含结果的响应。另请注意，即使t1和t2位于不同机器上，或者位于与x或y不同的机器上，发送t1 <- c(t2)也不会阻塞。
promise流水线应与并行异步消息传递区分开来。在支持并行消息传递但不支持流水线操作的系统中，上面示例中的消息发送x <- a()和y <- b()可以并行进行，但发送t1 <- c(t2)将不得不等到t1和t2都被接收，即使x、y、t1和t2在同一个远程机器上。在涉及许多消息的更复杂情况下，流水线的相对延迟优势变得更大。
promise流水线操作也不应与角色系统中的流水线消息处理相混淆，在这种系统中，角色可以在完成当前消息的处理之前指定并开始执行下一个消息的行为。

针对特定线程的future

某些语言（如Alice ML ）定义了与计算future值的特定线程相关联的future。 这种计算可以在创建future时及早开始，或者在首次需要其值时懒惰地开始。在延迟计算的意义上，懒惰的future类似于thunk 。
Alice ML还支持可由任何线程解决的future，并调用这些promise。 promise的这种使用不同于上文所述的在E语言中的使用。在Alice中，promise不是只读视图，并且不支持promise流水线操作。相反，对于future未来，包括与promise相关的future，流水线是自然而然地发生的。

相关结构

future是Event（同步原语）的特例，只能完成一次。通常，event可以重置为初始空状态，因此可以根据需要多次完成。
I-var（如在语言Id中）是具有上面定义的阻塞语义的future。I-structure是包含I-var的数据结构。可以使用不同值多次设置的相关同步构造称为M-var。M-var支持采用或放置当前值的原子操作，其中取值还将M-var设置回其初始空状态。 
并发逻辑变量与future类似，但是通过合一更新，与逻辑编程中的逻辑变量相同。因此，它可以多次绑定到可合一的值，但不能设置回空或unresolved状态。Oz的数据流变量充当并发逻辑变量，并且还具有上面提到的阻塞语义。
并发约束变量是并发逻辑变量的一般化，以支持约束逻辑编程：约束可以多次缩小，表示可能值的较小集合。通常，有一种方法可以指定每当约束进一步缩小时应该运行的hunk；这是支持约束传播所必需的。

不同形式future的表达能力之间的关系

通过在创建future的同时创建一个计算值的线程，可以在非线程特有的promise中直接实现及早求值的线程特有的future。在这种情况下，future将只读视图返回给客户端，以便仅让新创建的线程能够解决这个future。

要在非线程特有的promise中实现隐式延迟线程特有的promise（比如由Alice ML提供），需要一种机制来确定何时首先需要future的值（例如，Oz中的WaitNeeded构造 ）。
如果所有值都是对象，那么实现透明转发对象的能力就足够了，因为发送给转发器的第一条消息表明需要future的值。
假定系统支持消息传递，通过让resolve线程向future自己的线程发送消息，可以在线程特有的future中实现非线程特有的future。但是，这可以被视为不必要的复杂性。在基于线程的编程语言中，最具表现力的方法似乎是提供非线程特有的future，只读视图以及WaitNeeded构造或支持透明转发的混合。

求值策略

future的求值策略（可称为传future调用）是非确定性的：future的值将在创建future和使用其值之间的某个时间进行求值，但确切的时间不确定的，一次运行和另一次运行的求值时间会不一样。计算可以在创建future时开始（及早求值），或者仅在实际需要值时开始（懒惰求值），并且可以在中途暂停，或在一次运行中执行。一旦future被赋值，它就不会在访问future的时候重新计算；这就像传需求调用时使用的记忆化。
懒惰future是确定性具有惰性求值评估语义的future：future值的计算在首次需要时开始，与传需要调用一样。懒惰future在求值策略默认不是懒惰求值的语言中使用。例如，在C++11中，可以通过将std::launch::deferred启动策略传递给std::async以及计算值的函数来创建这种惰性future。

历史

future与promise构造首先在诸如MultiLisp和Act 1之类的编程语言中实现。在并发逻辑编程语言中使用逻辑变量进行通信非常类似于future。这些开始于Prolog with Freeze和IC Prolog，并成为真正的并发原语，包括关系语言、Concurrent Prolog、守卫霍恩子句（GHC）、Parlog、Strand、Vulcan、Janus、Oz-Mozart、Flow Java和Alice ML。来自数据流编程语言的单一赋值I-var ，源自Id并包含在Reppy的Concurrent ML中，非常类似于并发逻辑变量。
promise流水线技术（使用future来克服延迟）是Barbara Liskov和Liuba Shrira于1988年发明的，由Mark S. Miller、Dean Tribble和Rob Jellinghaus在大约1989年的Xanadu项目中独立发明。
promise一词是由Liskov和Shrira创造的，尽管他们通过名称call-stream引用了流水线机制，现在很少使用它。
Liskov和Shrira的论文中描述的设计以及Xanadu中的promise流水线的实现都有一个限制，即promise值不是一等的：一个参数，或者一个call或send返回的值不能直接成为一个promise （所以前面给出的promise流水线的例子，它使用一个发送的结果作为另一个发送的参数的承诺，在call-stream设计或Xanadu实现中不能直接表达）。似乎promise和call-stream从未在Argus的任何公开发布中实现，  Liskov和Shrira论文中使用的编程语言。Argus的开发在1988年左右停止了。 Xanadu实现的promise流水线仅在1999年Udanax Gold的源代码发布时才公开发布，并且在任何已发布的文档中都没有解释过。 Joule和E的后续实现支持完全一等的promise和resolver。
一些早期的角色语言，包括Act系列， 支持并行消息传递和流水线消息处理，但不支持promise流水线。（虽然技术上可以在前两个中实现最后一个功能，但没有证据表明Act语言这样做了。）
2000年之后，由于消息传递的请求-响应模型，future和promise在用户界面响应和web开发中的应用重新引起了人们的兴趣。现在有几种主流语言对future和promise都有语言支持，最着名的是Java 5中的FutureTask（2004年公布）以及.NET 4.5中的async和await结构（2010年发布，2012年发布）很大程度上受到F#的异步工作流程（可追溯到2007年）的启发。 随后被其他语言采用，特别是Dart（2014），Python（2015），  Hack（HHVM）以及ECMAScript 7（JavaScript）、Scala和C++的草案。

协程

future可以用协程或生成器实现， 从而产生相同的评估策略（例如，协同多任务或延迟评估）。

channel

future可以很容易地用channel实现：future是一个单元素的channel，而promise是一个发送到channel，实现future的过程。   这允许future在支持channel（如CSP和Go）的并发编程语言中实现。由此产生的future是显式的，因为它们必须通过从channel读取而不是仅仅通过评估来获取。