- Promises Basics
- Optimization Features
Promises are primary building blocks in the ActiveJ async programming model which can be compared to Java Futures.
- In general, Promise represents the result of an operation that hasn’t been completed yet, but will be completed at some undetermined point in the future. It is used for deferred and asynchronous computations.
- Promise is a high-performance Java Future alternative. It represent a future result of an asynchronous computation, and also allows to transform and process the unspecified upcoming result using chaining. Moreover, such results can be combined with the help of pre-defined combinators.
- Unlike Java Future, Promises were natively designed to work within a single eventloop thread. They are extremely lightweight, have no multithreading overhead, and can process millions of calls per second.
We can primarily manage Promises with the basic methods:
- of(T value) - creates a successfully completed promise, like CompletableFuture.completedFuture().
- ofException() - creates an exceptionally completed promise.
- complete() - creates a successfully completed Promise<Void>, a shortcut to Promise.of(null).
Promise will succeed or fail at some unspecified time and you need to chain methods that will be executed in both cases:
- then() - returns a new Promise which, when this Promise completes successfully, is executed with this Promise as an argument, like CompletionStage.thenCompose().
- map() - returns a new Promise which, when this Promise completes successfully, is executed with its result as an argument, like CompletionStage.thenApply().
- whenResult() - subscribes to execute given action after this Promise completes successfully, like CompletionStage.thenAccept().
In addition, to handle errors the following methods are provided:
- thenEx() - returns a new Promise which is executed with the Promise result as the argument when Promise completes either successfully or with an exception.
- whenException() - subscribe to execute given action after this Promise completes exceptionally and returns a new Promise.
If there are multiple asynchronous calls, we need to execute them in order. In this case you can simply chain methods to create a sequence.
There are cases when you need to execute several Promises and combine their results. For this purpose, consider the following methods:
- combine() - returns a new Promise that, when both Promises are completed, is executed with the two results as arguments.
- all() - returns a Promise that completes when all of the provided promises are completed.
- any() - returns one of the first completed Promises.
- delay() - delays completion of provided Promise for a defined period of time.
ActiveJ Promise is heavily GC-optimized:
- An internal representation of a typical Promise consists of 1-2 objects with a bare minimum of fields inside
- After Promise fulfills, it passes the result to its subscribers and than discards the result
In order to optimize Promises, there are several implementations of the Promise interface:
Promise- root interface which represents promises behaviour.
SettablePromise- can be used as a root for a chain of Promises. Allows to wrap operations in Promises, can be completed manually even before actual completion.
NextPromise- helper classes to create chains of stateless Promises. You can treat these chains as pipes which pass values through, but don’t store them.
CompletePromise- an abstract class which represents a successfully completed Promise.
CompleteNullPromise- helper classes.
We’ve compared ActiveJ Promise to Java CompletableFuture in different scenarios:
- ActiveJ Promise/Java CompletableFuture executes operations with one promise/future.
- ActiveJ Promise/Java CompletableFuture combines several promises/futures.
We used JMH as the benchmark tool and ran benchmarks in AverageTime mode. All the measurements are represented in nanoseconds.
You can find benchmark sources on GitHub.
$ git clone https://github.com/activej/activej
And import it as a Maven project. Check out branch master. Before running the examples, build the project.
These examples are located at activej -> examples -> core -> promise
You can create chains of Promises even before they are completed and you don’t know yet if they will complete successfully or with an exception. In this example we have a doSomeProcess which returns a Promise that has equal chances to complete either successfully or with an exception. So we create a chain which will handle both cases:
If you run the example, you will receive either this output (if doSomeProcess finishes successfully):
Or this, if it finishes with an exception:
Note that the first line is
This is due to the 1 second delay we set up in doSomeProcess.
You can combine several Promises, for example:
There are also several ways to delay Promise:
Promises is a helper class which allows to efficiently manage multiple Promises. This example will demonstrate three use cases.
1.In the following example we use the Promises loop, which resembles Java for loop, but has async capabilities, which are provided by Promise:
The output is:
2.Another example creates a list of Promises results using Promises toList method:
The output is:
3.In the last example Promises toArray method is utilized, which reduces promises to array of provided data type (in this case, Integers):
And the final output is:
Also, you can use Promises to work with file system. When you run this example :
… you’ll receive the following output, which represents content of the created file: