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讀取而不是僅僅通過評估來取得。