In computer science, futures, promises, delays, and deferreds are constructs used for synchronizing program execution in some concurrent programming languages. Each is an object that acts as a proxy for a result that is initially unknown, usually because the computation of its value is not yet complete.

The term promise was proposed in 1976 by Daniel P. Friedman and David Wise,Preliminary version of: and Peter Hibbard called it eventual. A somewhat similar concept future was introduced in 1977 in a paper by Henry Baker and Carl Hewitt.

The terms future, promise, delay, and deferred are often used interchangeably, although there can be some differences in usage between future and promise. Specifically, when usage is distinguished, a future is a read-only placeholder view of a variable, while a promise is a writable, single assignment container which sets the value of the future. Notably, a future may be defined without specifying which specific promise will set its value, and different possible promises may set the value of a given future, though this can be done only once for a given future. In other cases a future and a promise are created together and associated with each other: the future is the value, the promise is the function that sets the value – essentially the return value (future) of an asynchronous function (promise). Setting the value of a future is also called resolving, fulfilling, or binding it.

Applications Futures and promises originated in functional programming and related paradigms (such as logic programming) to decouple a value (a future) from how it was computed (a promise), allowing the computation to be done more flexibly, notably by parallelizing it. Later, it found use in distributed computing, in reducing the latency from communication round trips. Later still, it gained more use by allowing writing asynchronous programs in direct style, rather than in continuation-passing style.

Implicit vs. explicit Use of futures may be implicit (any use of the future automatically obtains its value, as if it were an ordinary reference) or explicit (the user must call a function to obtain the value, such as the get method of in Java). Obtaining the value of an explicit future can be called stinging or forcing. Explicit futures can be implemented as a library, whereas implicit futures are usually implemented as part of the language.

The original Baker and Hewitt paper described implicit futures, which are naturally supported in the actor model of computation and pure object-oriented programming languages like Smalltalk. The Friedman and Wise paper described only explicit futures, probably reflecting the difficulty of efficiently implementing implicit futures on stock hardware. The difficulty is that stock hardware does not deal with futures for primitive data types like integers. For example, an add instruction does not know how to deal with 3 + future factorial(100000). In pure actor or object languages this problem can be solved by sending future factorial(100000) the message +, which asks the future to add 3 to itself and return the result. Note that the message passing approach works regardless of when factorial(100000) finishes computation and that no stinging/forcing is needed.

Promise pipelining The use of futures can dramatically reduce latency in distributed systems. For instance, futures enable promise pipelining,Promise Pipelining at erights.orgPromise Pipelining on the C2 wiki as implemented in the languages E and Joule, which was also called call-stream Also published in ACM SIGPLAN Notices 23(7). in the language Argus.

Consider an expression involving conventional remote procedure calls, such as: t3 := ( x.a() ).c( y.b() ) which could be expanded to t1 := x.a(); t2 := y.b(); t3 := t1.c(t2); Each statement needs a message to be sent and a reply received before the next statement can proceed. Suppose, for example, that x, y, t1, and t2 are all located on the same remote machine. In this case, two complete network round-trips to that machine must take place before the third statement can begin to execute. The third statement will then cause yet another round-trip to the same remote machine.

Using futures, the above expression could be written t3 := (x <- a()) <- c(y <- b())

which could be expanded to t1 := x <- a(); t2 := y <- b(); t3 := t1 <- c(t2);

The syntax used here is that of the language E, where x <- a() means to send the message a() asynchronously to x. All three variables are immediately assigned futures for their results, and execution proceeds to subsequent statements. Later attempts to resolve the value of t3 may cause a delay; however, pipelining can reduce the number of round-trips needed. If, as in the prior example, x, y, t1, and t2 are all located on the same remote machine, a pipelined implementation can compute t3 with one round-trip instead of three. Because all three messages are destined for objects which are on the same remote machine, only one request need be sent and only one response need be received containing the result. The send t1 <- c(t2) would not block even if t1 and t2 were on different machines to each other, or to x or y.

Promise pipelining should be distinguished from parallel asynchronous message passing. In a system supporting parallel message passing but not pipelining, the message sends x <- a() and y <- b() in the above example could proceed in parallel, but the send of t1 <- c(t2) would have to wait until both t1 and t2 had been received, even when x, y, t1, and t2 are on the same remote machine. The relative latency advantage of pipelining becomes even greater in more complicated situations involving many messages.

Promise pipelining also should not be confused with pipelined message processing in actor systems, where it is possible for an actor to specify and begin executing a behaviour for the next message before having completed processing of the current message.

Read-only views In some programming languages such as Oz, E, and AmbientTalk, it is possible to obtain a read-only view of a future, which allows reading its value when resolved, but does not permit resolving it:

In Oz, the !! operator is used to obtain a read-only view. In E and AmbientTalk, a future is represented by a pair of values called a promise/resolver pair. The promise represents the read-only view, and the resolver is needed to set the future's value. In C++ (since C++11) a std::future<T> provides a read-only view. The value is set directly by using a std::promise<T>, or set to the result of a function call using std::packaged_task<R(Ts...)> or the std::async() function. C++26 introduces a proper std::execution::task<T, Env>. In the Dojo Toolkit's Deferred API as of version 1.5, a consumer-only promise object represents a read-only view. In Alice ML, futures provide a read-only view, whereas a promise contains both a future and the ability to resolve the future In .NET System.Threading.Tasks.Task<T> represents a read-only view. Resolving the value can be done via System.Threading.Tasks.TaskCompletionSource<T>.

Support for read-only views is consistent with the principle of least privilege, since it enables the ability to set the value to be restricted to subjects that need to set it. In a system that also supports pipelining, the sender of an asynchronous message (with result) receives the read-only promise for the result, and the target of the message receives the resolver.

Thread-specific futures Some languages, such as Alice ML, define futures that are associated with a specific thread that computes the future's value. This computation can start either eagerly when the future is created, or lazily when its value is first needed. A lazy future is similar to a thunk, in the sense of a delayed computation.

Alice ML also supports futures that can be resolved by any thread, and calls these promises. This use of promise is different from its use in E as described above. In Alice, a promise is not a read-only view, and promise pipelining is unsupported. Instead, pipelining naturally happens for futures, including ones associated with promises.

Blocking vs non-blocking semantics If the value of a future is accessed asynchronously, for example by sending a message to it, or by explicitly waiting for it using a construct such as when in E, then there is no difficulty in delaying until the future is resolved before the message can be received or the wait completes. This is the only case to be considered in purely asynchronous systems such as pure actor languages.

However, in some systems it may also be possible to attempt to immediately or synchronously access a future's value. Then there is a design choice to be made:

the access could block the current thread or process until the future is resolved (possibly with a timeout). This is the semantics of dataflow variables in the language Oz. the attempted synchronous access could always signal an error, for example throwing an exception. This is the semantics of remote promises in E. potentially, the access could succeed if the future is already resolved, but signal an error if it is not. This would have the disadvantage of introducing nondeterminism and the potential for race conditions, and seems to be an uncommon design choice.

As an example of the first possibility, in C++11, a thread that needs the value of a future can block until it is available by calling the wait() or get() member functions. A timeout can also be specified on the wait using the wait_for() or wait_until() member functions (in std::condition_variable) to avoid indefinite blocking. If the future arose from a call to std::async() then a blocking wait (without a timeout) may cause synchronous invocation of the function to compute the result on the waiting thread.

Related constructs Futures are a particular case of the synchronization primitive "events", which can be completed only once. In general, events can be reset to initial empty state and, thus, completed as many times as desired.500 lines or less, "A Web Crawler With asyncio Coroutines" by A. Jesse Jiryu Davis and Guido van Rossum says "implementation uses an asyncio.Event in place of the Future shown here. The difference is an Event can be reset, whereas a Future cannot transition from resolved back to pending."

An I-var (as in the language Id) is a future with blocking semantics as defined above. An I-structure is a data structure containing I-vars. A related synchronization construct that can be set multiple times with different values is called an M-var. M-vars support atomic operations to take or put the current value, where taking the value also sets the M-var back to its initial empty state.

A concurrent logic variable is similar to a future, but is updated by unification, in the same way as logic variables in logic programming. Thus it can be bound more than once to unifiable values, but cannot be set back to an empty or unresolved state. The dataflow variables of Oz act as concurrent logic variables, and also have blocking semantics as mentioned above.

A concurrent constraint variable is a generalization of concurrent logic variables to support constraint logic programming: the constraint may be narrowed multiple times, indicating smaller sets of possible values. Typically there is a way to specify a thunk that should run whenever the constraint is narrowed further; this is needed to support constraint propagation.

Relations between the expressiveness of different forms of future Eager thread-specific futures can be straightforwardly implemented in non-thread-specific futures, by creating a thread to calculate the value at the same time as creating the future. In this case it is desirable to return a read-only view to the client, so that only the newly created thread is able to resolve this future.