Пару недель назад разработчики tornado добавили нативный модуль по созданию inline callback (аналог inlineCallbacks в Twisted, Seq в Node.js, Fibers в Ruby).
Ниже примеры использования и примеры с участием asyncmongo (асинхронным драйвером для mongoDB)
Для сравнения приведу классический пример с калбеками
А так, с новым модулем «tornado.gen»
Вызовы сделаны через генераторы, подобно как в Twisted.
Так же можно вызывать массив методов, возврат управления происходит при отработке всех указанных методов
Ещё они добавили удобный механизм (методы Callback и Wait), для ожидания отработки всех нужных методов, запущенных в разное время
Все выше примеры были взяты из модуля tornado.gen
Пример как можно использовать вместе с asyncmongo
Здесь по очереди вызываются методы save и find_one, с получением результата.
Для следующего примера я сделал костыль-обертку, что-б использование mongodb было похоже на классическое ( db.user.save({ 'login':'tester' }), db.user.find_one({}) )
В отличие от предыдущего примера, в этом не нужно каждый раз проверять вернувшееся значение error, вместо него сработает исключение. (Хотя опять же нужно делать проверку на исключение :)
Кто захочет попробовать, модуль tornadomongo лежит тут: hg clone bitbucket.org/lega911/tornadomongo
В ней пришлось сделать «грязный inject» в модуль tornado, для возможности создания исключения, что позволяет этот модуль использовать только для экспериментов.
Я уже перевел некоторые модули одного проекта на tornado.gen, в итоге количество кода уменьшилось, читаемость кода повысилась (того же функционала).
PS: На момент написания статьи, модуль не был описан в документации, сейчас выложили: www.tornadoweb.org/documentation/gen.html
Ниже примеры использования и примеры с участием asyncmongo (асинхронным драйвером для mongoDB)
Для сравнения приведу классический пример с калбеками
class AsyncHandler(RequestHandler):
@asynchronous
def 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")
А так, с новым модулем «tornado.gen»
class GenAsyncHandler(RequestHandler):
@asynchronous
@gen.engine
def get(self):
http_client = AsyncHTTPClient()
response = yield gen.Task(http_client.fetch, "http://example.com")
do_something_with_response(response)
self.render("template.html")
Вызовы сделаны через генераторы, подобно как в Twisted.
Так же можно вызывать массив методов, возврат управления происходит при отработке всех указанных методов
def get(self):
http_client = AsyncHTTPClient()
response1, response2 = yield [gen.Task(http_client.fetch, url1),
gen.Task(http_client.fetch, url2)]
Ещё они добавили удобный механизм (методы Callback и Wait), для ожидания отработки всех нужных методов, запущенных в разное время
class GenAsyncHandler2(RequestHandler):
@asynchronous
@gen.engine
def 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")
Все выше примеры были взяты из модуля tornado.gen
Пример как можно использовать вместе с asyncmongo
class MainHandler(tornado.web.RequestHandler):
@property
def db(self):
if not hasattr(self, '_db'):
self._db = asyncmongo.Client(pool_id='mydb', host='127.0.0.1', \
port=27017, maxcached=10, maxconnections=50, dbname='test')
return self._db
@web.asynchronous
@gen.engine
def get(self):
r, error = yield gen.Task(self.db.user.save, { 'login':'tester' })
r, error = yield gen.Task(self.db.user.find_one, {})
self.write(str(r[0]))
self.finish()
Здесь по очереди вызываются методы save и find_one, с получением результата.
Для следующего примера я сделал костыль-обертку, что-б использование mongodb было похоже на классическое ( db.user.save({ 'login':'tester' }), db.user.find_one({}) )
class MainHandler(tornado.web.RequestHandler):
@property
def db(self):
if not hasattr(self, '_db'):
self._db = tornadomongo.mongo_client(pool_id='mydb', host='127.0.0.1', port=27017, maxcached=10, maxconnections=50, dbname='test')
return self._db
@web.asynchronous
@gen.engine
def get(self):
# save
r = yield self.db.user.save({ 'login':'tester' })
# find
try:
r = yield self.db.user.find_one({})
self.write(str(r))
except tornadomongo.MongoError as e:
self.write('error: '+str(e))
self.finish()
В отличие от предыдущего примера, в этом не нужно каждый раз проверять вернувшееся значение error, вместо него сработает исключение. (Хотя опять же нужно делать проверку на исключение :)
Кто захочет попробовать, модуль tornadomongo лежит тут: hg clone bitbucket.org/lega911/tornadomongo
В ней пришлось сделать «грязный inject» в модуль tornado, для возможности создания исключения, что позволяет этот модуль использовать только для экспериментов.
Я уже перевел некоторые модули одного проекта на tornado.gen, в итоге количество кода уменьшилось, читаемость кода повысилась (того же функционала).
PS: На момент написания статьи, модуль не был описан в документации, сейчас выложили: www.tornadoweb.org/documentation/gen.html