Source Code

Support for promises. If an operation cannot return a value immediately without blocking, it may instead return a promise of the value. A promise is an object that represents the return value or the thrown exception that the operation eventually produces. Such an operation is sometimes called a long-running operation.

This module provides following abstractions:

  • The Completable interface abstracts objects which promise one or more values, accommodating the possibility of failure.
  • The Completion interface abstracts Completables that may be combined to form a compound promise that produces multiple values.
  • The Promise class, a Completable that produces a single value, or fails.
  • The Deferred class, providing support for operations which return instances of the Promise interface.
  • The ExecutionContext class abstracts the concurrency of the runtime running the promises. The JVM runtime uses a threadpool, the JavaScript runtime uses the setTimeout function. The defineGlobalExecutionContext() can be use to change the default context

Promises

A Promise exists in one of three states:

  • In the promised state, the operation has not yet terminated.
  • In the fulfilled state, the operation has produced a value.
  • In the rejected state, the operation has terminated without producing a value. This situation is represented as an exception.

The method Promise.completed() allows interested parties to be notified when the promise makes a transition from the promised state to the fulfilled or the rejected state:

Promise<Document> promise = queryDocumentById(id);
promise.onComplete {
    (d) => print("Got the document: " + d.title);
    (e) => print("Document was not received: " + e.message);
};

The first function is called the onFulfilled callback and the second function is called the onRejected callback. The onRejected function is always optional.

Returning promises

A Deferred object is a factory that provides an instance of the Promise class and manages its lifecycle, providing operations to force its transition to a fulfilled or rejected state.

The instance of Deferred should remain private to the long-running operation, only the Promise should be exposed to the caller.

The Promise of a deferred can be retrieved from its promise field:

value deferred = Deferred<String>();
return deferred.promise;

The Deferred object implements the Completable interface which provides two methods for controlling the state of the promise:

For example:

value deferred = Deferred<String>();
void doOperation() {
    try {
        String val = getValue();
        deferred.fulfill(val);
    }
    catch (Throwable e) {
        deferred.reject(e);
    }
}

Chaining promises

When composition is needed the method Completion.map() should be used instead of the Completion.completed() method.

When invoking the Completion.map() method the onFulfilled and onRejected callbacks can return a value. The compose() method returns a new promise that will be fulfilled with the value of the callback. This promise will be rejected if the callback invocation fails.

For example:

Promise<Integer> promiseOfInteger = promiseOfInteger();
Promise<String> promiseOfString = promiseOfInteger.compose((i) => i.string);
promiseOfString.compose((s) => print("Completed with " + s));

Or, more concisely:

promiseOfInteger()
    .compose((i) => i.string)
    .compose((s) => print("Completed with " + s));

Composing promises

Promises can be composed into a single promise that is fulfilled when every one of the individual composed promises is fulfilled. If one of the promise is rejected then the composed promise is rejected.

Promise<String> promiseOfInteger = promiseOfString();
Promise<Integer> promiseOfString = promiseOfInteger();
(promiseOfInteger.and(promiseOfString)).completed {
    (i, s) => print("All fulfilled");
    (e) => print("One failed");
};

Notice that:

  • The order of the parameters in the callback is in reverse order in which the corresponding promises are chained.
  • The return type of combined promise is not Promise but Completable.

The onComplete() method

The onComplete() method of a promise allows a single callback to be notified when the promise is fulfilled or rejected.

Promise<Document> promise = queryDocumentById(id);
promise.onComplete {
    void (Document|Throwable result) {
        switch (result)
        case (is Document) { print("Fulfilled"); }
        case (is Throwable) { print("Rejected"); }
    };
 };

Promise.onComplete() is most useful for implementing a finally clause in a chain of promises.

Feeding with a promise

Deferred can be transitioned with a promise instead of a value:

Deferred<String> deferred1 = getDeferred1();
Deferred<String> deferred2 = getDeferred2();
deferred1.fulfill(deferred2);

Similarly the callback may return a promise instead of a value:

Deferred<String> deferred = Deferred<String>();
promise.compose((s) => deferred.promise);

Thread safety

The implementation is thread safe and uses a non blocking algorithm for maintaining the state of a Deferred object.

Relationship to the A+ specification

This module is loosely based upon the A+ specification, with the following differences:

  • The then() method is split between Completion.map() that returns an object and Completion.flatMap() that can return a Promise
  • The Promise Resolution Procedure is implemented for objects or promises but not for thenables since that would require a language with dynamic typing.
By: Julien Viet
License: Apache Software License
Packages
ceylon.promise
Dependencies
java.base (jvm)7
Values
globalExecutionContextSource Codeshared ExecutionContext globalExecutionContext

The global execution context for running promise compositions when no execution context is explicitly used

Functions
addGlobalExecutionListenerSource Codeshared Anything() addGlobalExecutionListener(ExecutionListener onChild)

Add a global execution listener that will be used when composing promises. The execution listener is invoked at composition time and returns a function that will wrap a later execution of the composed function.

This function returns a function that when called removes the global execution listener.

defineGlobalExecutionContextSource Codeshared void defineGlobalExecutionContext(ExecutionContext context)

Define the global execution context for running deferred compositions

Interfaces
CompletableSource Codeshared Completable<in Value>

Something that can go through a transition and is meant to be be completed, i.e either fulfilled or rejected.

CompletionSource Codeshared Completion<out Element,out T>

Completion provides base support for promises and their composition. This interface satisfies the Promised interface, to be used when a Promise is needed

ExecutionContextSource Codeshared ExecutionContext

The execution context

ExecutionListenerSource Codeshared ExecutionListener
PromisedSource Codeshared Promised<out Value>

An object that provides a Promised.promise.

Classes
DeferredSource Codeshared Deferred<Value>

The deferred class is the primary implementation of the Promise interface.

The promise is accessible using the promise attribute of the deferred.

The deferred can either be fulfilled or rejected via the Completable.fulfill() or Completable.reject() methods. Both methods accept an argument or a promise to the argument, allowing the deferred to react on a promise.

PromiseSource Codeshared abstract Promise<out Value>

A promise represents a value that may not be available yet. The primary method for interacting with a promise is its Promise.map() method. A promise is a Completion element restricted to a single value.