liveBook · Manning

Continuation

As explained in C++ Concurrency in Action, Second Edition

A continuation is a function scheduled to run automatically after an asynchronous computation reaches a specified completion point.

A continuation is a function scheduled to run when an asynchronous result becomes ready. Instead of calling `wait()` or polling a future, attach the next operation with `fut.then(continuation)`. This expresses “when the data is ready, process it” without making the calling thread wait for that result. section 4.4.3

In the Concurrency TS, `std::experimental::future` supports `then()`. The continuation receives the ready future as its sole argument and calls `get()` to retrieve its value. Attaching it consumes the original future: `fut` becomes invalid, while `then()` returns a new future for the continuation’s result. The continuation may run on an unspecified thread, so do not assume which thread executes it. section 4.4.3 section 4.4.4

std::experimental::future<int> find_the_answer;
auto fut=find_the_answer();
auto fut2=fut.then(find_the_question);
assert(!fut.valid());
assert(fut2.valid());

The validity checks show the ownership change: after `then()`, only the future returned for the continuation remains valid. section 4.4.3

Asynchronous login

Authenticate a user, request account information, then update the display by chaining each operation with `then()`. Each continuation calls `get()` on its input future. If an earlier operation throws, that exception can propagate through the chain to the final continuation’s error handler. section 4.4.4

Listing 4.21. A function to process user login with fully asynchronous operations
std::experimental::future<void> process_login(
    std::string const& username,std::string const& password)
{
    return backend.async_authenticate_user(username,password).then(
        [](std::experimental::future<user_id> id){
            return backend.async_request_current_info(id.get());
        }).then([](std::experimental::future<user_data> info_to_display){
            try{
                update_display(info_to_display.get());
            } catch(std::exception& e){
                display_error(e);
            }
        });
}

Here the backend operations themselves return futures. Compare this with a chain whose backend calls still block: continuations alone do not eliminate threads waiting inside those calls. section 4.4.4

Continuations also work after futures are combined. `std::experimental::when_all` produces a future that becomes ready when every supplied future is ready; a continuation can then gather their results. `when_any` enables work when the first result arrives, including a continuation that checks remaining futures if needed. section 4.4.5 section 4.4.6

How C++ Concurrency in Action, Second Edition covers it

How deep it goes
Works through `then()` ownership, arguments, chaining and execution-thread uncertainty, then identifies the limit posed by blocking backend calls.
Examples
Uses future-validity checks, a chained login workflow, and gathering results from multiple futures.
What it assumes
Assumes familiarity with C++ functions and futures, while motivating continuations through the drawbacks of waiting on a future.
What you can do after
Apply it via `std::experimental::future::then()` to chain asynchronous operations and handle their results or exceptions.
How it connects
Connects futures and exception propagation with asynchronous operations, `when_all`, and `when_any`.

Also explained in

Compare how 3 books explain continuation →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of C++ Concurrency in Action, Second Edition. The section links point to the parts of the book it draws on.