Future与promise

title: 应用.
content: future和promise起源于函数式编程和相关范例(如逻辑编程 ),目的是将值(future)与其计算方式(promise)分离,从而允许更灵活地进行计算,特别是通过并行化。后来,它在分布式计算中得到了应用,减少了通信往返的延迟。再后来,它变得更有用了,因为它允许以直接风格编写异步程序,而不是以连续传递风格的方式。
title: 隐式与显式.
content: 对future的使用可能是"隐式的"(任何对future的使用都会自动获得它的值,它就像是普通的引用一样)或"显式的"(用户必须调用函数来获取值,例如Java中的或的codice_1方法)。获得一个显式的future的值可以称为"stinging"或"forcing"。显式future可以作为库来实现,而隐式future则通常作为语言的一部分来实现。  最初的Baker和Hewitt论文描述了隐式future,它们在演员模型和纯面向对象编程语言(如Smalltalk)中自然得到支持。Friedman和Wise的论文只描述了显式的future,可能反映了在老旧硬件上有效实施隐式future的困难。难点在于老旧硬件不能处理原始数据类型(如整数)的future。例如,add指令不会处理codice_2 。在纯角色模型或面向对象语言中,这个问题可以通过发送codice_3消息codice_4,它要求future自己加codice_5并返回结果。请注意,无论codice_6何时完成计算,消息传递方法都可以工作,而且不需要任何sting或force。
title: promise流水线.
content: 在分布式系统中使用future可以显著地减少延迟。例如,future让"promise流水线"成为了可能, 就像在E语言和Joule语言中实现的那样,在Argus语言中这也被称为"call-stream"。  考虑一个涉及常规远程过程调用的表达式,例如:  可以扩展为  每个语句需要发送一条消息,并在下一个语句可以继续之前收到一个答复。例如,假设codice_7、codice_8、codice_9和codice_10都位于同一台远程机器上。在这种情况下,在开始执行第三条语句之前,必须对该机器进行两次完整的网络往返。然后,第三条语句将引起另一个到同一个远程机器的往返。  使用future,上面的表达式可以写成 可以扩展为 这里使用的语法是E语言的语法,其中codice_11表示将消息codice_12异步发送给codice_7。所有三个变量都会立即为其结果分配future,执行过程将继续进行到后面的语句。之后尝试解决codice_14的值可能会导致延迟;但是,流水线操作可以减少所需的往返次数。如果与前面的示例一样,codice_7、codice_8、codice_9和codice_10都位于相同的远程机器上,则流水线实现可以用一次往返来计算codice_14,不必用三次。由于所有三条消息都指向同一远程计算机上的对象,因此只需要发送一个请求,只需要接收一个包含结果的响应。另请注意,即使codice_9和codice_10位于不同机器上,或者位于与codice_7或codice_8不同的机器上,发送codice_24也不会阻塞。  promise流水线应与并行异步消息传递区分开来。在支持并行消息传递但不支持流水线操作的系统中,上面示例中的消息发送codice_11和codice_26可以并行进行,但发送codice_24将不得不等到codice_9和codice_10都被接收,即使codice_7、codice_8、codice_9和codice_10在同一个远程机器上。在涉及许多消息的更复杂情况下,流水线的相对延迟优势变得更大。  promise流水线操作也不应与角色系统中的流水线消息处理相混淆,在这种系统中,角色可以在完成当前消息的处理之前指定并开始执行下一个消息的行为。
title: 只读视图.
content: 在某些编程语言(如Oz\E和AmbientTalk)中,可以获得未来的"只读视图",该视图允许在resolve后读取其值,但不允许resolve它: !!运算符用于获得只读视图  对只读视图的支持符合最小特权原则,因为它允许将值设置为仅限于需要设置该值的主体。在同样支持流水线的系统中,异步消息的发送方(包括结果)接收结果的只读承诺promise,消息的目标接收resolver。
title: 针对特定线程的future.
content: 某些语言(如Alice ML )定义了与计算future值的特定线程相关联的future。 这种计算可以在创建future时及早开始,或者在首次需要其值时懒惰地开始。在延迟计算的意义上,懒惰的future类似于thunk 。  Alice ML还支持可由任何线程解决的future,并调用这些"promise"。 "promise"的这种使用不同于上文所述的在E语言中的使用。在Alice中,promise不是只读视图,并且不支持promise流水线操作。相反,对于future未来,包括与promise相关的future,流水线是自然而然地发生的。
title: 阻塞与非阻塞语义.
content: 如果future的值是异步访问的,例如通过向它发送消息,或者通过使用类似于E语言中的codice_41的构造显式地等待它,那么在收到消息或完成等待之前,推迟到future得到resolve没有任何困难。这是在纯异步系统(如纯角色语言)中唯一需要考虑的情况。  然而,在某些系统中,还可能尝试"立即"或"同步"访问未来的值。这样的话就需要做出一个设计选择:  作为第一种可能性的示例,在C++11中,需要future值的线程可以通过调用codice_42或codice_43成员函数来阻塞,直到可用为止。您还可以使用codice_44或codice_45成员函数指定等待超时,以避免无限期阻塞。如果future对codice_38的调用,那么阻塞等待(没有超时)可能导致函数的同步调用以计算等待线程上的结果。
title: 相关结构.
content: "future"是Event(同步原语)的特例,只能完成一次。通常,event可以重置为初始空状态,因此可以根据需要多次完成。  "I-var"(如在语言Id中)是具有上面定义的阻塞语义的future。"I-structure"是包含I-var的数据结构。可以使用不同值多次设置的相关同步构造称为"M-var"。M-var支持"采用"或"放置"当前值的原子操作,其中取值还将M-var设置回其初始"空"状态。  "并发逻辑变量"与future类似,但是通过合一更新,与逻辑编程中的"逻辑变量"相同。因此,它可以多次绑定到可合一的值,但不能设置回空或unresolved状态。Oz的数据流变量充当并发逻辑变量,并且还具有上面提到的阻塞语义。  "并发约束变量"是并发逻辑变量的一般化,以支持约束逻辑编程:约束可以多次"缩小",表示可能值的较小集合。通常,有一种方法可以指定每当约束进一步缩小时应该运行的hunk;这是支持"约束传播"所必需的。
title: 不同形式future的表达能力之间的关系.
content: 通过在创建future的同时创建一个计算值的线程,可以在非线程特有的promise中直接实现及早求值的线程特有的future。在这种情况下,future将只读视图返回给客户端,以便仅让新创建的线程能够解决这个future。 要在非线程特有的promise中实现隐式延迟线程特有的promise(比如由Alice ML提供),需要一种机制来确定何时首先需要future的值(例如,Oz中的codice_47构造 )。 如果所有值都是对象,那么实现透明转发对象的能力就足够了,因为发送给转发器的第一条消息表明需要future的值。  假定系统支持消息传递,通过让resolve线程向future自己的线程发送消息,可以在线程特有的future中实现非线程特有的future。但是,这可以被视为不必要的复杂性。在基于线程的编程语言中,最具表现力的方法似乎是提供非线程特有的future,只读视图以及"WaitNeeded"构造或支持透明转发的混合。
title: 求值策略.
content: future的求值策略(可称为"传future调用")是非确定性的:future的值将在创建future和使用其值之间的某个时间进行求值,但确切的时间不确定的,一次运行和另一次运行的求值时间会不一样。计算可以在创建future时开始(及早求值),或者仅在实际需要值时开始(懒惰求值),并且可以在中途暂停,或在一次运行中执行。一旦future被赋值,它就不会在访问future的时候重新计算;这就像传需求调用时使用的记忆化。  懒惰future是确定性具有惰性求值评估语义的future:future值的计算在首次需要时开始,与传需要调用一样。懒惰future在求值策略默认不是懒惰求值的语言中使用。例如,在C++11中,可以通过将codice_48启动策略传递给codice_38以及计算值的函数来创建这种惰性future。
title: 角色模型中的future语义.
content: 在角色模型中,形式为codice_50的表达式由它对codice_51消息(环境为"E",客户为"C")的响应方式定义:future表达式通过向客户"C"发送新创建的角色来响应codice_51消息"F" (计算codice_53的响应的代理)作为返回值,"与此同时"向codice_53发送环境"E"和客户"C"的codice_51消息。"F"的默认行为如下:  但是,一些future可以通过特殊方式处理请求以提供更大的并行性。例如,表达式codice_58可以创建一个新的future,其行为类似于数字codice_59 。这个技巧并不总是有效。例如,以下条件表达式: 会挂起,直到codice_61这个future已回应询问codice_62是否大于其自身的请求。
title: 历史.
content: "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中的codice_63(2004年公布)以及.NET 4.5中的codice_64和codice_65结构(2010年发布,2012年发布)很大程度上受到F#的"异步工作流程"(可追溯到2007年)的启发。 随后被其他语言采用,特别是Dart(2014),Python(2015), Hack(HHVM)以及ECMAScript 7(JavaScript)、Scala和C++的草案。
title: 实现列表.
content: 支持future、promise、并发逻辑变量、数据流变量或I-vars的语言,无论是通过直接语言支持还是在标准库中,包括:  还支持promise流水线的语言包括:  基于非标准库的future实现:
title: 协程.
content: future可以用协程或生成器实现, 从而产生相同的评估策略(例如,协同多任务或延迟评估)。
title: 通道.
content: future可以很容易地用通道实现:future是一个单元素的通道,而promise是一个发送到通道,实现future的过程。 这允许future在支持通道(如CSP和Go)的并发编程语言中实现。由此产生的future是显式的,因为它们必须通过从通道读取而不是仅仅通过评估来获取。

Summary: 在计算机科学中,future、promise、delay和deferred是指用于在某些并发编程语言中同步程序执行的构造。由于某些计算(或者网络请求)尚未结束,我们需要一个对象来代理这个未知的结果,于是就有了上述这些构造(future、promise等)。

Future (Programmierung): Ein Future (engl. ‚Zukunft‘) oder ein Promise (engl. ‚Versprechen‘) bezeichnet in der Programmierung einen Platzhalter ("Proxy") für ein Ergebnis, das noch nicht bekannt ist, meist weil seine Berechnung noch nicht abgeschlossen ist.