While discussing this, the natural way to write this kind of code in Twisted is actually to return Deferreds from a callback (the asyncio equivalent would be to return a Future from a done callback). In Twisted, if a callback returns a Deferred the next callback in the chain is not called until the Deferred resolves, at which point it is called with the result of that Deferred. Essentially, then, the program shown above returns only a single Deferred with a long series of callbacks that transform the data, some of which operate asynchronously.
As best as I can work out, the only reason that asyncio Future’s don’t behave this way is that asyncio strongly emphasises the use of coroutines to program in this manner. Put another way, the asyncio-native way to write that code is not to chain Futures, but instead to write a coroutine that manages your flow control for you:
async def do_work():
result = await check_cache(url)
if not result:
result = await do_web_request(url)
print(‘done: {}’.format(f.result()))
loop.run_until_complete(do_work())
This is not a reason in and of itself not to have chain_futures as a thing that exists. However, I think it may be the case that the asyncio core developers aren’t hugely interested in it. I’m now reading a bit into the mindset of Guido so I’m sure he could step in and correct me, but I seem to recall that asyncio’s Futures were deliberately designed to have less of the complexity of, say, Twisted’s Deferreds, in part because coroutines were intended to supersede some of the more complex functionality of Deferreds.
Cory