Facebook has recently introduced Folly Futures, a C++11 library providing an implementation of futures that goes beyond what already offered by std::future
.
A future is a construct used for synchronization of concurrent operations. It can be viewed as a read-only proxy object to the result of an aynchronous operation whose value is initially unknown. If the future’s client tries to read its value before the future has been fulfilled, it may block. A future is usually associated to a promise, which provides sort-of write-access to the future’s value.
An asynchronous operation can immediately return a read-only future without blocking, as in the following snippet:
#include <folly/futures/Future.h>
using folly::Future;
Future<Output> asyncOperation(Input);
Future<Output> f = asyncOperation(input);
where asyncOperation
is a wrapper around an async call. The future’s client can then access it to check if its related Promise
has already been fulfilled using the isReady()
method, and access its value using value()
.
A future is created from its associated promise, whose value can be set when ready using either setValue()
or setWith()
:
using folly::Promise;
Future<double> getEnergy(int year) {
auto promise = make_shared<Promise<double>>();
std::thread([=]{
promise->setWith(std::bind(getEnergySync, year));
}).detach();
return promise->getFuture();
}
The real power of Folly Futures can be unleashed when using the Future::then
method, which allows to chain callbacks in an easy way that prevents callback hell. This is how chaining can be expressed:
Future<OutputA> futureA(Output);
Future<OutputB> futureB(OutputA);
Future<OutputC> futureC(OutputB);
OutputD d(OutputC) {
if (somethingExceptional) throw anException;
return OutputD();
}
Future<double> fut =
fooFuture(input)
.then(futureA)
.then(futureB)
.then(futureC)
.then(d)
.then([](OutputD outputD) { // lambdas are ok too
return outputD * M_PI;
});
Another powerful building block that Folly Futures provides is the collect
method, which allows to treat a collection of futures as one single future that will be fulfilled when all the composing futures have been. Similar to collect
, Folly Futures also provides:
collectAny
: it completes as soon as any of the input futures completes.collectN
: it allows to wait for N of the input futures to complete.map
: it takes a collection of futures and a function and callthen()
with that function on each input futures. It returns another array of futures.reduce
: it takes a collection of futures and a function that takes two arguments (the reduced value and the next value in the reducing sequence) and apply that function to each future in turn.
Finally, Folly Futures also supports the notion of an execution context to control how callbacks are executed. For example, you can pass an executor
object to then
to specify that its callback shall be executed through that executor
:
struct Executor {
using Func = std::function<void()>;
virtual void add(Func) = 0;
};
a(input).then(executor, b);
More information can be found in Folly Future’s documentation.