The code included in this question shows a chained set of callbacks using boost::asio. In this case the code is very clear because the length of the chain is short.
In real world applications the chain of callbacks is often much longer, and must include callbacks for timeouts and code to handle errors or badly formatted messages. This quickly becomes very complex, analogous to a 1960s design with too many gotos.
Some of the complexity can be factored out by splitting the code into layers, but since the design is asynchronous, a chain of callbacks of some sort is inevitable somewhere.
Or is it? Are there ways of reducing the complexity of an asynchronous design? (Obviously in some cases using threads would help, but I'm looking for a single-threaded solution.)