In Tornado, we usually write the following code to call a function asynchronously:
class MainHandler(tornado.web.RequestHandler): @tornado.gen.coroutine def post(self): ... yield self.handleRequest(foo) ... @tornado.gen.coroutine def handleRequest(self, foo): ... But in asyncio (will be shipped with Python 3.4, can be installed from pip for Python 3.3), we use yield from to achieve the same thing:
@asyncio.coroutine def myPostHandler(): ... yield from handleRequest(foo) ... @asyncio.coroutine def handleRequest(foo) ... Seeing from the code, the difference is yield and yield from. However the former handleRequest(foo) returns a tornado.concurrent.Future object, the latter returns a generator object.
My question is, what is the difference between the two things in mechanism? How is the control flow? And who calls the actual handleRequest and retrieves its returning value?
Append: I have basic knowledge of Python generators and iterators. I wanted to understand what Tornado and asyncio achieved by using these, and what is the difference between those two mechanisms.
There is a huge difference between the two. yield from takes another generator and continues yielding from that generator instead (delegating responsibility, as it were). yield just yields one value.
In other words, yield from, in the simplest case, could be replaced by:
for value in self.handleRequest(foo): yield value If you replaced a yield from <expression> line with yield <expression> you'd return the whole generator to the caller, not the values that generator produces.
The yield from syntax was only introduced in Python 3.3, see PEP 380: Syntax for Delegating to a Subgenerator. Tornado supports Python versions 2.6, 2.7 and 3.2 in addition to Python 3.3, so it cannot rely on the yield from syntax being available. asyncio, on the other hand, being a core Python library added in 3.4, can fully rely on the yield from generator delegation syntax being available.
As a result, Tornado will have to post-process values yielded from a @tornado.gen.coroutine generator to detect that a tornado.concurrent.Future object was yielded; the @asyncio.coroutine code handling can be much simpler. And indeed the Tornado Runner.run() method does explicit type checks to handle delegated tasks.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With