facebook/tornado — pull request #513 by jjwchoy (2012-05-24 01:50:31+00:00)
Title: Promises - Syntactic sugar for tornado.gen

Here is the first version of my promises module, exposing a simpler (I hope!) api to `tornado.gen`.

This isn't ready for inclusion yet (no tests/documentation), but I've opened this to get some initial feedback.

The public api consists of two functions: `promise` and `until` and one class `Promise`. Here is an example of it in action:

from tornado.web import asynchronous
from tornado import gen
from tornado.promises import promise, until

from app_specific import fetch_user
from app_specific import fetch_blog
from app_specific import fetch_blog_comments

class GenAsyncHandler(RequestHandler):
@asynchronous
@gen.engine
def get(self, blog_id):
user = promise(fetch_user, self.get_current_user)
blog = promise(fetch_blog, blog_id)

yield until(blog)
blog = blog.value
comments = promise(fetch_blog_comments, blog.blog_id)
author = promise(fetch_user, blog.author)

yield until(user, comments, author)
user = user.value
comments = comments.value
author = author.value

self.render('blog_post.html', user=user, blog=blog, comments=comments, author=author)

The promise function has the same args as the constructor for gen.Task.

blog = promise(fetch_blog, blog_id)

This calls fetch_blog immediately and returns a `Promise` object. You then use the `until` function in conjunction with `yield` to yield control until the blog post has been fetched.

yield until(blog)

`until` accepts a variable number of `Promise` arguments. So you can wait for multiple promises to be fulfilled:

yield until(user, comments, author)

Getting the results
--------------------------

The promise object stores the *args and **kwargs given to the callback. The *args are made available through the `values` property. For convenience if only one positional argument was provided the `value` property can be used. If more than one positional argument was provided than `value` behaves the same as `values`.

Named arguments are made accessible through attributes on the promise object. (If the name clashes with an existing attribute name on the `Promise` object (`value`, `values` or `finished` for example), then there is currently no public way to access it, but it would be trivial to add another property which exposes the kwargs.

The access to the named arguments makes error checking pretty easy:

yield until(blog)

if blog.error:
self.send_error()
return

blog = blog.value

Feedback Wanted
--------------------------

- What do you think of the names `promise` and `until`? I think `yield until(...)` makes for very readable code. Do you agree?
- What do you think of the interface of the `Promise` class?
- What changes would you make?