I've submitted a new module that lets you write asynchronous code in a straight-line synchronous style using generators. It is similar in spirit to libraries such as adisp, monocle, and swirl; its major distinguishing feature is the option to split a single asynchronous call into two steps (Callback/Wait), which affords greater flexibility in control flow and parallelism.Please check it out; I'd appreciate feedback on the interface before everything gets frozen for 2.1.-Ben"""``tornado.gen`` is a generator-based interface to make it easier towork in an asynchronous environment. Code using the ``gen`` moduleis technically asynchronous, but it is written as a single generatorinstead of a collection of separate functions.For example, the following asynchronous handler::class AsyncHandler(RequestHandler):@asynchronousdef get(self):http_client = AsyncHTTPClient()http_client.fetch("http://example.com",callback=self.on_fetch)def on_fetch(self, response):do_something_with_response(response)self.render("template.html")could be written with ``gen`` as::class GenAsyncHandler(RequestHandler):@asynchronous@gen.enginedef get(self):http_client = AsyncHTTPClient()response = yield gen.Task(http_client.fetch("http://example.com"))do_something_with_response(response)self.render("template.html")`Task` works with any function that takes a ``callback`` keyword argument(and runs that callback with zero or one arguments). For more complicatedinterfaces, `Task` can be split into two parts: `Callback` and `Wait`::class GenAsyncHandler2(RequestHandler):@asynchronous@gen.enginedef get(self):http_client = AsyncHTTPClient()http_client.fetch("http://example.com",callback=(yield gen.Callback("key"))response = yield gen.Wait("key")do_something_with_response(response)self.render("template.html")The ``key`` argument to `Callback` and `Wait` allows for multipleasynchronous operations to proceed in parallel: yield severalcallbacks with different keys, then wait for them once all the asyncoperations have started."""
> Please check it out; I'd appreciate feedback on the interface before
> everything gets frozen for 2.1.
I think my only feedback has to do with the name 'gen' itself. Given
the module's usage, the fact that it's generator-based is an important
detail but not a descriptor unto itself.
I think the names would be a bit more obvious to newcomers if the
module was simply called "tornado.async":
@gen.engine => @async.function
gen.Task => async.Task
gen.Wait => async.Wait
"tornado.async" is my current preference, but I appreciate how that
could be consider ambiguous alongside the existing @asynchronous
decorator. Perhaps someone else can think of a better name (hopefully
without this turning into a "naming-the-thing" thread <g>).
Other than that detail, this looks quite good, and I appreciate the
upfront effort you put into including such descriptive exceptions.
--
Jon Parise (jon of indelible.org) :: "Scientia potentia est"
I agree with Jon, the module name should describe it's purpose (and
function) not implementation details.
+1 for tornado.async instead of tornado.gen
-alex
--
a lex 13 x
http://www.a13x.info
Perhaps someone else can think of a better name (hopefully
without this turning into a "naming-the-thing" thread <g>).
I haven't tried it yet, but I guess it can be used as a replacement
for `adisp`? I'll try to implement this into Momoko after I'm done
with the blocking connection part.
I like how it handles multiple async operations, much better than adisp.
What happens with exceptions thrown in the async handler? It would be
nice to have them appear back in the original context that started the
operation. One potential problem are exceptions in functions that
issue multiple async operations in parallel.
I echo other's comments about renaming the module to async, it's a
better name for what the module does.
Ovidiu
On Sun, Sep 4, 2011 at 12:07 PM, Ben Darnell <b...@bendarnell.com> wrote:
What happens with exceptions thrown in the async handler? It would be
nice to have them appear back in the original context that started the
operation. One potential problem are exceptions in functions that
issue multiple async operations in parallel.
Naming this kind of thing is tricky, and I'm open to suggestions, but I definitely prefer "gen" to "async". This module needs a more specific name than "async" because I could easily imagine other things going into an "async" module (e.g. https://gist.github.com/741041), many of which are alternatives to the generator approach rather than complementary (in addition to the potential confusion with @tornado.web.asynchronous and the now-deprecated async_callback methods). I think "gen" is a reasonable descriptor since the salient difference to the application developer between using this module and manually passing callbacks around is that you're writing a generator.
Naming this kind of thing is tricky, and I'm open to suggestions, but I definitely prefer "gen" to "async".