Future与promise

在计算机科学中，future、promise、delay和deferred，是在某些并发编程语言中，指称用于同步Execution (computing)的一种构造。由于某些计算尚未结束，故而需要一个对象来代理这个未知的结果。这种构造起源于函数式编程和相关范型如逻辑编程，其目的是将值与其运算过程解耦，从而允许更灵活地进行计算，特别是通过将其并行化来进行。后来它在分布式计算中得到了应用，用来减少网络通信往返的延迟。再后来async/await语法机制使其变得更有用，籍此允许以直接风格编写异步程序，而不再采用续体传递风格。

**术语**
在1976年Daniel P. Friedman和David Wise提出了术语“promise”，同年Peter Hibbard称之为“eventual”。1977年Henry Baker和在一篇论文中介绍了一个类似的概念“future”。
术语“future”、“promise”、“delay”和“deferred”通常用来称谓同样一种机制，在特定实现中可能只选用其中之一。在这种情况下，值与其运算过程是一起创建并且相互关联的：future若指称推迟设置的一个值，设置它的函数就可称谓为promise；promise若指称推迟设置的一个值，设置它的函数就可称谓为resolver；promise若指称一个异步函数，它的结果值所设置的就可称谓为future。设置一个推迟值也称为“决定/解决”（resolve）、“履行/实现”（fulfil）或“绑定”（bind）它。
推迟值及其设置者也存在区分称谓而同时使用的情况，二者的这种用法在不同实现中的差异将在专门章节中讨论。具体来说，future指称一个“只读”的变量的占位符视图，而promise指称一个可写的单赋值容器，使用其“成功”方法来设置这个future的值。尤其是在定义future之时，无须指定设置其值的promise；并且可能有不同的promise，设置同一个future的值；但是对于一个给定future，仅可以进行一次设置。

**历史**
future及/或promise构造，首先实现于编程语言例如MultiLisp和Act 1之中。在Concurrent logic programming语言中，使用非常类似于future的逻辑变量进行通信。这种语言开始于1982年的“Prolog with Freeze”和“IC Prolog”，并且在后来的语言中变成了真正的并发原语，比如Concurrent Prolog、Parlog、守卫霍恩子句（GHC）、Strand、Janus、Oz（Mozart）和Alice MLA state is a finite function σ mapping addresses a to so-called units u. Units are either primitive values other than names or representations of records, variants, reference cells, functions, and primitive operations …… A match Match is a sequence of clauses (p1 ⇒ e1  …  pk ⇒ ek). ……We now extend DML with logic variables, one of the essentials of logic programming. Logic variables are a means to represent in a state partial information about the values of addresses. Logic variables are modelled with a new unit lvar. The definition of states is extended so that a state may map an address also to lvar or an address. ……A match …… blocks until the store contains enough information to commit to one of the clauses or to know that none applies.|access-date=2024-03-13|archive-date=2024-03-13|archive-url=https://web.archive.org/web/20240313032810/https://www.ps.uni-saarland.de/Papers/abstracts/Smolka-Oz-SML-98.html|dead-url=no。叫做“I-变量”的单赋值变量，非常类似于并发逻辑变量，它起源于数据流程编程语言Id的“I-结构”元件An I-structure component has a single assignment restriction, i.e. it can only be assigned once, at which point its state goes from empty to full. Any attempt to assign it more than once is caught as a runtime error. The component can be read an arbitrary number of times.……An M-structure component can be assigned with a put operation and read with a take operation. A value can be put only into an empty component — it is a runtime error if it is already full. Many takes may be attempted concurrently on a component.|access-date=2024-03-13|archive-date=2024-05-19|archive-url=https://web.archive.org/web/20240519120048/https://sylvain-huet.com/rsc/mit/memo-284-2.pdf|dead-url=no，并且包含在Reppy的Concurrent ML中。
在1988年Barbara Liskov和Liuba Shrira，发明了promise流水线技术来克服传输延迟Call-streams allow a sender to make a sequence of calls to a receiver without waiting for replies. The stream guarantees that the calls will be delivered to the receiver in the order they were made and that the replies from the receiver will be delivered to the sender in call order. ……The design of promises was influenced by the future mechanism of MultiLisp5. Like futures, promises allow the result of a call to be picked up later. However, promises extend futures in several ways: Promises are strongly typed and thus avoid the need for runtime checking to distinguish them from ordinary values. They allow exceptions from the called procedure to be propagated in a convenient manner. Finally, they are integrated with the call-stream mechanism and address problems such as node failures and network partitions that do not arise in a single-machine environment. ……There are two reasons for using stream calls instead of RPCs: they allow the caller to run in parallel with the sending and processing of the call, and they reduce the cost of transmitting the call and reply messages. RPCs and their replies are sent over the network immediately, to minimize the delay for a call. Stream calls and their replies, however, are buffered and sent when convenient; in the case of sends, normal replies can be omitted. Buffering allows us to amortize the overhead of kernel calls and the transmission delays for messages over several calls, especially for small calls and replies. | access-date= 2024-01-26 | archive-date= 2024-01-26 | archive-url= https://web.archive.org/web/20240126063638/https://dl.acm.org/doi/10.1145/960116.54016 | dead-url= no  Also published in ACM SIGPLAN Notices 23(7).；Liskov和Shrira在论文中使用了术语“promise”，但是他们采用了现在少见的名称“call-stream”来提及流水线机制。在大约1989年Mark S. Miller、Dean Tribble和Rob Jellinghaus，于Xanadu项目中也独立发明了此技术。
Liskov和Shrira的论文中描述的设计，以及Xanadu中的promise流水线的实现，都有一个限制，即promise值不是头等的：call或send的参数或返回值，不能直接是promise。在Liskov和Shrira论文中使用的编程语言Argus，直至大约1988年停止开发，似乎都未曾在任何公开发布中实现promise和call-stream。Xanadu实现的promise流水线，仅在1999年Udanax Gold的源代码发布时才公开发布，并且在任何已发布的文档中都没有解释过。
一些早期的演员语言，包括Act系列，支持并行消息传递和流水线式消息处理，但不支持promise流水线。Joule和E语言的后续实现，支持完全头等的promise和resolver，还有promise流水线。
2000年之后，由于Messaging pattern的请求-响应模型，在用户界面Responsiveness和Web开发中的应用，future和promise重新引起了人们的兴趣。现在一些主流语言对future和promise有了语言支持，最著名的是2004年发行的Java 5中的FutureTask，以及2012年发行的.NET框架 4.5中的async/await结构，它在很大程度上受到可追溯到2007年的“F#异步编程模型”的启发。async/await随后被其他语言采用，特别是2014年的Dart 1.9、2014年发行的Hack（HHVM）、2015年的Python 3.5、ECMAScript 2017、2019年的Rust 1.39和C++20等。

**实现列表**

**编程语言支持**
下面列出支持future、promise和并发逻辑变量、数据流程变量或I-变量的语言，包括直接在语言中支持还有在标准库中支持：- ABCL/f- Alice ML，支持futureWe will be concerned with futures and promises, which differ from general logic variables in that a distinction is made between reading and writing them. Bidirectional unification can be replaced by (single-) assignment.|access-date=2024-03-14|archive-date=2024-03-14|archive-url=https://web.archive.org/web/20240314032515/https://www.ps.uni-saarland.de/Papers/abstracts/Schwinghammer-Diplom.html|dead-url=no- AmbientTalk，包含头等的resolver和只读promise- C++，从C++11开始有std::future和std::promise- Concurrent ML，仅限I-变量和M-变量- Crystal- Dart，使用Future/Completer类以及关键字async/await- Elm，通过Task模块
*Glasgow Haskell，仅限同步可变变量“MVars”- Id，仅限I-结构和M-结构- Io- Java，通过Future或CompletableFuture- JavaScript，始于ECMAScript 2015提供Promise对象，并且自从ECMAScript 2017可通过关键字async/await来使用- Lucid，仅限作为串流的变量
*一些Lisp
** Clojure
** MultiLisp
** Racket- .NET，自.NET框架 4.0起通过TPL（Task Parallel Library）
** C#，自.NET框架 4.5起，通过关键字async/await
** Visual Basic，自从版本11，通过关键字Async/Await- Kotlin，但是kotlin.native.concurrent.Future通常只在书写本机运行代码时使用- Nim，使用async/await语法- Oxygene- Oz版本3 - Python，自从2011年版本3.2提供concurrent.futures模块，这是由PEP 3148所提议Futures are created by concrete implementations of the Executor class (called ExecutorService in Java). The reference implementation provides classes that use either a process or a thread pool to eagerly evaluate computations. |access-date=2018-12-31 |archive-date=2018-12-31 |archive-url=https://web.archive.org/web/20181231092706/https://www.python.org/dev/peps/pep-3148/ |dead-url=no ，而2015年版本3.5增加了基于低层future的asyncio模块，它采用高层async/await语法- R，延迟计算的promise，仍然是单线程- Raku- Rust，通常经由.await完成- Scala，通过scala.concurrent包- Scheme，仅延迟求值- Squeak Smalltalk- Strand
还支持promise流水线的语言包括：- E- Joule

**非标准库的实现**
对于Common Lisp：
** Blackbird
** Eager Future2
** lparallel
** PCall
*对于C++：
** Boost library
** Dlib
** Qt
** Seastar
** Folly
** POCO C++ Libraries，ActiveResult
*对于C#和其他.NET语言：
**Parallel Extensions库
*对于Groovy：
**GPars
*对于JavaScript：
**Promises/A+标准网站，有此标准的实现的列表
**Cujo.js的when.js，提供的promise符合Promises/A+ 1.1标准
**Dojo Toolkit，提供promise和Twisted风格Deferred
** MochiKit，受Twisted的Deferred的启发 
** jQuery的Deferred对象，基于了CommonJS Promises/A规定
** AngularJS
**Q，符合Promises/A+ 1.1标准
**RSVP.js，符合Promises/A+ 1.1标准
**Bluebird
**Closure库的promise包，符合Promises/A+标准
*对于Java：
**JDeferred，提供了与JQuery.Deferred对象类似的deferred-promise API和行为 
**ParSeq，提供task-promise API，由LinkedIn维护，适用于异步流水线和分支
*对于Objective-C：
**MAFuture、RXPromise、ObjC-CollapsingFutures、PromiseKit、objc-promise、OAPromise
*对于OCaml：
**Lazy模块实现了懒惰的显式future
*对于Perl：
**Future、Promises、Reflex
*对于PHP：
**React/Promise 
*对于Python：
** promise
** Twisted的Deferred
*对于R：
**future，实现可扩展的future API与懒惰和及早同步和（多核或分布式）异步future 
*对于Ruby：
** Promise gem
**libuv gem，实现promise
** Celluloid gem，实现future
** future-resource
*对于Rust：
** futures-rs
*对于Scala：
**Twitter的util库
*对于Swift：
**异步框架，实现C#风格的async/非阻塞await
**FutureKit，Apple的GCD（Grand Central Dispatch）实现版本
**FutureLib，纯Swift 2库实现的Scala风格的future和promise与TPL风格的取消
**Deferred，纯Swift库，受到OCaml的Deferred启发 
**BrightFutures 
*对于Tcl：
**tcl-promise

**自行实现**
future可以用协程或生成器实现，从而具有相同的求值策略（例如协同多任务或延迟求值）。
future还可以很容易地用通道实现：future是一个单元素的通道，而promise是一个发送到通道，实现future的过程。这允许future在支持通道（如CSP和Go）的并发编程语言中实现。由此产生的future是显式的，因为它们必须通过从通道读取而不是仅仅通过求值来获取。

**编程语言典型示例**

**Python**
Python的concurrent.futures模块，提供了异步可调用对象的高层接口。异步执行可以使用线程池执行器ThreadPoolExecutor，通过多个线程来进行；或使用进程池执行器ProcessPoolExecutor，通过分立的多个进程来进行。concurrent.futures.Future类封装了可调用对象的异步执行，其实例由执行器抽象类Executor的这两个子类的submit()方法创建。
在下面的例子中，定义了load_url()，用来检索一个URL所指定的单一网页并报告其内容。使用with语句指定采用线程池执行器，这确保了线程由它及时清理。用这个执行器的submit()方法启动所有的装载操作任务，并使用字典推导式为其创建的每个future标记上对应的URL。采用模块函数as_completed()，在指定的Future类的诸实例“已齐全”（completed）之时，建立在其上的迭代器。
import concurrent.futures
import urllib.request
URLS = 
'https://www.python.org/',
'https://pypi.org/search/',
'https://no-such-url'

def load_url(url, timeout):
with urllib.request.urlopen(url, timeout=timeout) as conn:
return conn.read()
with concurrent.futures.ThreadPoolExecutor() as executor:
future_to_url = {executor.submit(load_url, url, 60) : url for url in URLS}
for future in concurrent.futures.as_completed(future_to_url):
url = future_to_urlfuture
try:
data = future.result()
except Exception as exc:
print(f'{url!r} generated an exception: {exc!s}')
else:
print(f'{url!r} page is {len(data):d} bytes')
在CPython中，由于全局解释器锁（GIL），保证一时只有一个线程可以执行Python字节码；一些扩展模块被设计为在进行计算密集任务时释放GIL，还有在进行I/O时总是释放GIL。线程池执行器的max_workers参数的缺省值是min(32, os.cpu_count() + 4)，这个缺省值为I/O bound任务保留至少5个worker线程，为释放GIL的CPU踊跃任务利用最多32个CPU逻辑核心。想要更好利用多核心机器的计算资源，可以使用进程池执行器或多进程模块multiprocessing。对于同时运行多个I/O bound任务，仍然适合采用线程池执行器或线程模块threading。
Python的异步I/O模块asyncio，是采用async/await语法编写并发代码的库。asyncio模块基于低层事件循环，提供的高层API包括了：并发运行Python协程并拥有在其执行上的完全控制，进行网络IO和IPC，控制子进程，通过队列分布任务和同步并发代码。asyncio模块的同步原语，在设计上类似于threading模块的同步原语，但与之相比有两个重要差异：asyncio模块的同步原语不是线程安全的，故而不能用于OS线程同步；这些同步原语的方法不接受超时实际参数。
asyncio模块提供的低层API中有asyncio.Future对象，用来桥接低层基于回调的代码和高层采用async/await语法的代码，它在设计上模仿了concurrent.futures.Future对象。这两个模块的Future对象的set_result()方法标记这个Future对象为“已完毕”（done）并设置它的结果，而set_exception()方法标记这个Future对象为已完毕并设置一个例外；done()方法在这个Future对象已完毕时返回True；result()方法返回这个Future对象所设置的结果，或者引发其所设置的例外。
二者的关键差异包括了：asyncio.Future是可期待（awaitable）对象，而concurrent.futures.Future对象不可以被期待（awaited）。asyncio.Future.result()不接受超时实际参数，如果此刻这个Future对象的结果仍不可获得，它引发一个InvalidStateError例外；而concurrent.futures.Future.result()，可接受超时（timeout）实际参数，如果此刻结果仍未“完全”（completed），它等待指定时间后若仍未完全则引发TimeoutError例外，如果超时未指定或指定为None，则对等待时间没有限制。

**JavaScript**
在JavaScript中，Promise对象表示一个异步运算的最终完成或失败，及其结果值或失败理由。Promise对象是对一个值的代理（proxy），这个值在创建这个promise之时不必需已知。promise允许为异步行动的最终成功值或失败理由关联上处理器，这使得异步方法像同步方法那样返回值：并非立即返回最终值，异步方法返回一个promise来在将来的某一点上提供这个值。
在JavaScript生态系统中，Promise对象在成为语言本身的一部份之前很久就有了多种实现dead-url=no。尽管各有不同的内部表示，至少几乎所有类似Promise的对象都实现了“可接续”（thenable）接口。可接续对象实现了.then()方法，Promise对象就是可接续的。
一个promise被称为已落实（settled），如果它要幺已履行（fulfilled）要幺已拒绝（rejected），而不再待定（pending）。Promise实例的.then()方法，接受一到二个实际参数，它们是作为处理器异步执行的回调函数，分别针对了这个promise的已履行和已拒绝情况；此方法返回一个新的promise，它决定（resolve）出其所调用处理器的返回值，或者在原来这个promise未被处理的情况下，仍决定出其已落实的值。Promise实例的.catch()方法，实际上就是置空针对已履行情况的回调函数，而只有针对已拒绝情况的回调函数的.then()方法。
Promise()构造子创建Promise对象，它主要用于包装仍基于回调的API所提供的异步运算，要注意它只能通过new算子来构造：new Promise(executor)。Promise()构造子的实际参数叫做“执行器”（executor），它是由这个构造子同步执行的一个函数，它有可自行指定名字的两个函数形式参数，比如指定为resolveFunc和rejectFunc，这两个函数接受任何类型的单一实际参数。
Promise()构造子返回的Promise对象，在要幺resolveFunc函数要幺rejectFunc函数被调用之时，它就成为“已决定”（resolved）的。要注意如果在调用这两个函数之一的时候，将另一个Promise对象作为实际参数传递给它，则这个Promise对象可以称为已决定但仍未落实。在执行器中有任何错误抛出，都会导致这个promise成为已拒绝状态，而返回值会被忽略。
Promise类提供四个静态方法来实施异步任务并发，其中的Promise.allSettled()方法，接受有多个promise的可迭代对象作为输入，当所有输入的promise都已落实之时，返回一个单一的已履行的Promise对象，它决定出描述每个输入promise的结果的一个对象数组；其中每个对象都有status属性，它是字符串"fulfilled"或者"rejected"；还有在status为"fulfilled"时出现的属性value，或者在status为"rejected"时出现的属性reason。
在下面例子中，全局fetch()方法，启动从网络上取回一个资源的过程，并返回一旦响应可获得就履行的一个promise；这个promise决定出表示对这个请求响应的一个Response对象，它只在遇到网络错误之时拒绝。Response.text()方法返回一个promise，它决定出这个响应的主体的一个文本表示。
const urls = 
'https://developer.mozilla.org/',
'https://javascript.info/promise-api',
'https://no-such-url'

const fetchPromises = urls.map((url) => fetch(url));
Promise.allSettled(fetchPromises)
.then((results) => { 
results.forEach((result, num) => {
if (result.status == "fulfilled") {
if (result.value.ok) { 
result.value.text()
.then((data) => {
console.log(`${urlsnum}: ${data.length} bytes`);
})
.catch((err) => {
console.error(err);
});
} 
else {
console.log(`${urlsnum}: ${result.value.status}`);
}
}
else if (result.status == "rejected") {
console.error(`${urlsnum}: ${result.reason}`);
}
});
});
JavaScript是天然的单线程的，所以在给定时刻只有一个任务在执行，但控制可以在不同的promise之间转移，使得多个promise表现为并发执行。在JavaScript中并行执行只能通过分立的worker后台线程来完成dead-url=nodead-url=no。
同基于promise的代码合作的一种更简单的方式，是使用async/await关键字。在函数开始处增加async，使其成为异步函数，异步函数总是返回一个promise。在异步函数中，可以在对返回一个promise的函数的调用之前，使用await关键字；这使得代码在这一点上等待直到这个promise已落实下来，然后在这一点上promise的已履行的值被当作返回值，而已拒绝的理由被作为例外抛出。可以使用try...catch块进行例外处理，就如同这个代码是同步的一样。

**只读视图**
在某些编程语言（如Oz、E和AmbientTalk）中，可以获得推迟值的“只读视图”，允许在解决（resolve）出这个值之后通过它来读取，但不允许通过它来解决这个值：
*在Oz语言中，
运算符用于获得只读视图。
*在E语言和AmbientTalk中，推迟值由一对称为“promise/resolver对”的值表示。promise表示只读视图，需要resolver来设置推迟值。
*在C++11中，std::future提供了一个只读视图。该值通过使用std::promise直接设置，或使用std::packaged_task或std::async设置为函数调用的结果。
*在Dojo Toolkit的1.5版本的Deferred API中，“仅限consumer的promise对象”表示只读视图。
*在Alice ML中，future提供“只读视图”，而promise包含future和解决future的能力。
*在.NET 4.0中，System.Threading.Tasks.Task表示只读视图。解决值可以通过System.Threading.Tasks.TaskCompletionSource来完成。
对只读视图的支持符合最小特权原则，因为它使得设置值的能力仅限于需要设置该值的主体。在同样支持流水线的系统中，异步消息（具有结果）的发送方接收结果的只读promise，消息的目标接收resolver。

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

**隐式与显式future**
对future的使用可以是“隐式”的，任何对future的使用都会自动获得它的值，它就像是普通的引用一样；也可以是“显式”的，用户必须调用函数来获取值，例如Java中的Future或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)何时完成计算，消息传递方法都可以工作，而且不需要任何“刺激”或“强迫”。

**阻塞与非阻塞语义**
如果future的值是异步访问的，例如通过向它发送消息，或者通过使用类似于E语言中的when的构造显式地等待它，那幺在消息可以被接收或等待完成之前，推迟直到future得到解决（resolve）是没有任何困难的。这是在纯异步系统（如纯演员语言）中唯一需要考虑的情况。
然而，在某些系统中，还可能尝试“立即”或“同步”访问future的值。这样的话就需要做出一个设计选择：
*访问可能会阻塞当前线程或进程，直到future得到解决（可以具有超时）。这是Oz语言中“数据流变量”的语义。
*尝试的同步访问总是会引发信号指示错误，例如抛出异常。这是E语言中远程promise的语义。
*潜在的，如果future已经解决，则访问可能成功，但如果未解决，则发出信号指示错误。这样做的缺点是引入了不确定性和潜在的竞争条件，这似乎是一种不常见的设计选择。
作为第一种可能性的示例，在C++11中 ，需要future值的线程可以通过调用wait()或get()成员函数来阻塞，直到它可获得为止。还可以使用wait_for()或wait_until()成员函数指定等待超时，以避免无限期阻塞。如果future对std::async的调用，那幺阻塞等待（没有超时）可能导致函数的同步调用以计算等待线程上的结果。

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

**有特定线程的future**
某些语言比如Alice ML，定义的future可以关联着计算这个future值的特定线程。这种计算可以通过spawn exp，在创建future时及早地开始；或者通过lazy exp，在首次需要其值时懒惰地开始。在延迟计算的意义上，懒惰的future类似于thunk 。
Alice ML还支持可由任何线程解决的future，并称谓它们为“promised future”。这里的promise是给future的显式把柄（handle），它通过多态库函数Promise.promise来创建，所有promise都关联的一个future，创建一个新promise也就创建了一个新鲜的future；这个future通过Promise.future来提取，并且通过显式的应用Promise.fulfill于对应的promise来消除，即在全局中将这个future替代为一个值。Alice ML不支持promise流水线，转而对于future，包括关联着promise的future，流水线是自然而然地发生的。
在没有特定线程的future（如Alice ML所提供的）中，通过在创建这个future的同时创建一个计算这个值的线程，可以直接实现及早求值的有特定线程的future。在这种情况下，最好将只读视图返回给客户，以便仅让新创建的线程能够解决这个future。
要在没有特定线程的future中，实现隐式惰性的有特定线程的future，需要一种机制来确定何时首次需要future的值（例如，Oz中的WaitNeeded构造 ）。
如果所有值都是对象，那幺有实现透明转发对象的能力就足够了，因为发送给转发器的首条消息表明需要future的值。
假定系统支持消息传递，在有特定线程的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语义**
在演员模型中，形式为future 的表达式，以它对具有环境E和客户C的Eval消息的响应方式来定义：future表达式通过向客户C发送新创建的演员F（计算的响应的代理）作为返回值来响应Eval消息，与之并发的向发送具有环境E和客户F的Eval消息。F的默认行为如下：
*当F收到请求R时，它会检查是否已经收到来自求值的响应（可以是返回值或抛出异常），处理过程如下所示：
*#如果它已经有了响应V，那幺
*#*如果V是返回值，那幺向它发送请求R。
*#*如果V是一个异常，那幺就把这个异常抛给请求R的客户。
*#如果它还没有响应，那幺将R存储在F内的请求队列中。
*当F接收到来自求值的响应V时，那幺将V存储在F中，并且
**如果V是返回值，那幺将所有排队的请求发送到V。
**如果V是一个异常，那幺就会把这个异常抛出给每个排队请求的客户。
但是，一些future可以通过特殊方式处理请求以提供更大的并行性。例如，表达式1 + future factorial(n)可以创建一个新的future，其行为类似于数字1+factorial(n) 。这个技巧并不总是有效。例如，以下条件表达式：
 if m>future factorial(n) then print("bigger") else print("smaller")
会挂起，直到factorial(n)这个future已回应询问m是否大于其自身的请求。

**参见**
监视器 (程序同步化)
*诅咒金字塔 (编程)

**引用**

**外部链接**
 Concurrency patterns presentationdate=20140714202338  given at  scaleconfdate=20200817135139 -  Future Valuedate=20120314080520  and  Promise Pipeliningdate=20050925100747  at the Portland Pattern Repository-  Easy Threading with Futuresdate=20071210090520  in Python