Future与promise

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

## 术语

在1976年Daniel P. Friedman和David Wise提出了术语“promise”，同年Peter Hibbard称之为“eventual”。1977年Henry Baker和Carl Hewitt在一篇论文中介绍了一个类似的概念“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之中。在并发逻辑编程语言中，使用非常类似于future的逻辑变量进行通信。这种语言开始于1982年的“Prolog with Freeze”和“IC Prolog”，并且在后来的语言中变成了真正的并发原语，比如Concurrent Prolog、Parlog、守卫霍恩子句（GHC）、Strand语言、Janus语言、Oz（Mozart）和Alice语言。叫做“I-变量”的单赋值变量，非常类似于并发逻辑变量，它起源于数据流程编程语言Id语言的“I-结构”元件，并且包含在Reppy的Concurrent ML中。

在1988年Barbara Liskov和Liuba Shrira，发明了promise流水线技术来克服传输延迟；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年之后，由于消息模式的请求-响应模型，在用户界面响应力和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等。

## 编程语言典型示例

### Python

Python的`concurrent.futures`模块，提供了异步 (计算机编程)程序执行可调用对象的高层接口。异步执行可以使用线程池执行器`ThreadPoolExecutor`，通过多个线程来进行；或使用进程池 (计算机科学)执行器`ProcessPoolExecutor`，通过分立的多个进程来进行。`concurrent.futures.Future`类封装了可调用对象的异步执行，其实例由执行器抽象类`Executor`的这两个子类的`submit()`方法创建。

在下面的例子中，定义了`load_url()`，用来检索一个URL所指定的单一网页并报告其内容。使用`with`语句指定采用线程池执行器，这确保了线程由它及时清理。用这个执行器的`submit()`方法启动所有的装载操作任务，并使用字典推导式为其创建的每个future标记上对应的URL。采用模块函数`as_completed()`，在指定的`Future`类的诸实例“已齐全”（completed）之时，建立在其上的迭代器。

```python
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_url[future]
        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踊跃任务保留至少5个worker线程，为释放GIL的CPU踊跃任务利用最多32个CPU逻辑核心。想要更好利用多核心机器的计算资源，可以使用进程池执行器或多进程模块`multiprocessing`。对于同时运行多个I/O踊跃任务，仍然适合采用线程池执行器或线程模块`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`对象在成为语言本身的一部分之前很久就有了多种实现。尽管各有不同的内部表示，至少几乎所有类似`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，它决定出这个响应的主体的一个文本表示。

```javascript
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(`${urls[num]}: ${data.length} bytes`);
                })
                .catch((err) => {
                    console.error(err);
                });
            } 
            else {
                console.log(`${urls[num]}: ${result.value.status}`);
            }
        }
        else if (result.status == "rejected") {
            console.error(`${urls[num]}: ${result.reason}`);
        }
    });
});
```

JavaScript是天然的单线程的，所以在给定时刻只有一个任务 (计算)在程序执行，但控制可以在不同的promise之间转移，使得多个promise表现为并发执行。在JavaScript中并行执行只能通过分立的worker后台线程来完成。

同基于promise的代码合作的一种更简单的方式，是使用async/await关键字。在函数开始处增加`async`，使其成为异步函数，异步 (计算机编程)函数总是返回一个promise。在异步函数中，可以在对返回一个promise的函数的调用之前，使用`await`关键字；这使得代码在这一点上等待直到这个promise已落实下来，然后在这一点上promise的已履行的值被当作返回值，而已拒绝的理由被作为例外抛出。可以使用`try...catch`块进行例外处理，就如同这个代码是同步的一样。

## 实现列表

### 编程语言支持

下面列出支持future、promise和并发逻辑变量、数据流程变量或I-变量的语言，包括直接在语言中支持还有在标准库中支持：

- ABCL/f
- Alice语言，支持future
- 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（任务并行库）
- 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所提议，而2015年版本3.5增加了基于低层future的`asyncio`模块，它采用高层`async`/`await`语法
- R，延迟计算的promise，仍然是单线程
- Raku
- Rust，通常经由`.await`完成
- Scala，通过scala.concurrent包
- Scheme，仅延迟求值
- Squeak Smalltalk
- Strand语言

还支持promise流水线的语言包括：

- E
- Joule语言

### 非标准库的实现

- Blackbird
- Eager Future2
- lparallel
- PCall
- Boost library
- Dlib
- Qt
- Seastar
- Folly
- POCO C++ Libraries，ActiveResult
- 对于C#和其他.NET语言：
- Parallel Extensions库
- GPars
- 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
- Google Closure工具的promise包，符合Promises/A+标准
- JDeferred，提供了与JQuery.Deferred对象类似的deferred-promise API和行为
- ParSeq，提供task-promise API，由LinkedIn维护，适用于异步流水线和分支
- MAFuture、RXPromise、ObjC-CollapsingFutures、PromiseKit、objc-promise、OAPromise
- Lazy模块实现了懒惰的显式future
- Future、Promises、Reflex
- React/Promise
- promise
- Twisted的Deferred
- future，实现可扩展的future API与懒惰和及早同步和（多核或分布式）异步future
- Promise gem
- libuv gem，实现promise
- Celluloid gem，实现future
- future-resource
- futures-rs
- Twitter的util库
- 异步框架，实现C#风格的`async`/非阻塞`await`
- FutureKit，Apple的GCD（Grand Central Dispatch）实现版本
- FutureLib，纯Swift 2库实现的Scala风格的future和promise与TPL风格的取消
- Deferred，纯Swift库，受到OCaml的Deferred启发
- BrightFutures
- tcl-promise

### 自行实现

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

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

## 只读视图

在某些编程语言（如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语言中，future提供“只读视图”，而promise包含future和解决future的能力。
- 在.NET 4.0中，`System.Threading.Tasks.Task<T>`表示只读视图。解决值可以通过`System.Threading.Tasks.TaskCompletionSource<T>`来完成。

对只读视图的支持符合最小特权原则，因为它使得设置值的能力仅限于需要设置该值的主体。在同样支持流水线的系统中，异步消息（具有结果）的发送方接收结果的只读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 <- a()) <- c(y <- b())
```

其中`x <- a()`表示将消息`a()`异步发送给`x`。它可以展开为：

```
 t1 := x <- a();
 t2 := y <- b();
 t3 := t1 <- c(t2);
```

所有三个变量都会立即为其结果分配promise，执行过程将继续进行到后面的语句。之后尝试解决`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语言，定义的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 <Expression>`的表达式，以它对具有环境E和客户C的`Eval`消息的响应方式来定义：future表达式通过向客户C发送新创建的演员F（计算`<Expression>`的响应的代理）作为返回值来响应`Eval`消息，与之并发的向`<Expression>`发送具有环境E和客户F的`Eval`消息。F的默认行为如下：

- 当F收到请求R时，它会检查是否已经收到来自求值`<Expression>`的响应（可以是返回值或抛出异常），处理过程如下所示：
- 如果它已经有了响应V，那么
- 如果V是返回值，那么向它发送请求R。
- 如果V是一个异常，那么就把这个异常抛给请求R的客户。
- 如果它还没有响应，那么将R存储在F内的请求队列中。
- 当F接收到来自求值`<Expression>`的响应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 presentation given at scaleconf
- Future Value and Promise Pipelining at the Portland Pattern Repository
- Easy Threading with Futures in Python