Combining Asyncly Futures
Why use Futures vs Callbacks?
- callback hell
- callback interface hell
- less code
- more readable code
- composabilty
Composability
- chaining
- transforming
- splitting
- joining
Async Computation Graph Notation
Future A
Future B
Future C
operationA()
operationB()
combinator()
Async Computation Graph Notation
ResponseA
ResponseB
ResponseAny
getHttpUrl(serviceA)
getHttpUrl(serviceB)
when_any()
Chaining
DiscoveryResponse
DiscoveredServiceResponse
getHttpUrl(discoveryService)
getHttpUrl(discoveredUrl)
asyncly::Future<Response> getHttpUrl(const std::string& url);
void run() {
getHttpUrl("https://discovery-service.com/discover-some-service")
.then(
[](Response responseFromDiscoveryService) {
// note that we return another future
return getHttpUrl(responseFromDiscoveryService.body);
})
.then(
[](Response response) {
// do something with the response here
std::cout << response.body << std::endl;
});
}
Chaining
Chaining
- chains can be as long as you want
- don't nest .then(), always move continuations to the outside
- the continuation passed to then() should always take arguments as values (we support move semantics, ie T that are not copyable).
- T must be movable or copyable
- Futures themselves are copyable, but you must only ever call then() once on all copies
- then() can return a value instead of a future, that's converted into a resolved future automatically
- futures can hold void
Splitting
DiscoveryResponse1
DiscoveredServiceResponse1
getHttpUrl(discoveryService)
getHttpUrl(discoveredUrl1 + "/1")
DiscoveryResponse2
DiscoveredServiceResponse2
getHttpUrl(discoveredUrl + "/2")
DiscoveryResponse
asyncly::split()
asyncly::Future<Response> getHttpUrl(const std::string& url);
void run() {
auto serviceUrlFuture =
getHttpUrl("https://discovery.com/some-service");
// split consumes one future and returns a tuple of two futures
// T must be copyable!
auto serviceUrlPair = asyncly::split(std::move(serviceUrlFuture));
auto endpointAFuture = std::get<0>(serviceUrlPair)
.then([](auto serviceUrl) {
return getHttpUrl(serviceUrl.body + "/1");
});
auto endpointBFuture = std::get<1>(serviceUrlPair)
.then([](auto serviceUrl) {
return getHttpUrl(serviceUrl.body + "/2");
});
// proceed with doing something with the two responses
}
Splitting
when_any
ResponseA
ResponseB
ResponseAorB
getHttpUrl(serviceA)
getHttpUrl(serviceB)
when_any()
asyncly::Future<Response> getHttpUrl(const std::string& url);
void run() {
auto responseFutureA = getHttpUrl("https://service-a");
auto responseFutureB = getHttpUrl("https://service-b");
asyncly::when_any(
std::move(responseFutureA),
std::move(responseFutureB))
.then([](Response result) {
std::cout << result.body << std::endl;
});
}
when_any
when_all
ResponseA
ResponseB
ResponseAandB
getHttpUrl(serviceA)
getHttpUrl(serviceB)
when_all()
asyncly::Future<Response> getHttpUrl(const std::string& url);
void run() {
auto responseFutureA = getHttpUrl("https://service-a");
auto responseFutureB = getHttpUrl("https://service-b");
asyncly::when_all(
std::move(responseFutureA),
std::move(responseFutureB))
.then([](Response responseA, Response responseB) {
std::cout << responseA.body << std::endl;
std::cout << responseB.body << std::endl;
});
}
when_all
Composablity
DiscoveryResponse1
DiscoveredServiceResponse1
getHttpUrl(discoveryService)
getHttpUrl(discoveredUrl1 + "/1")
DiscoveryResponse2
DiscoveredServiceResponse2
getHttpUrl(discoveredUrl + "/2")
DiscoveryResponse
asyncly::split()
asyncly::when_all()
BothResponses
asyncly::split()
asyncly::add_timeout()
ResponsesOrTimeout
Combining Asyncly Futures
By Jupp Müller
Combining Asyncly Futures
- 342