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