tornado.gen.engine 与 tornado.gen.coroutine 的区别
根据tornado.gen的文档,有没有人能帮我理解一下tornado.gen.coroutine和tornado.gen.engine之间的具体区别是什么?
1 个回答
8
根据tornado文档中关于gen.engine
的说明:
这个装饰器和协程很像,但它不返回一个Future对象,而且回调参数也没有特别处理。
而gen.coroutine
的文档中提到:
从调用者的角度来看,@gen.coroutine就像是@return_future和@gen.engine的组合。
简单来说,gen.engine
是一个比较旧的、功能不够完善的版本,而协程则是更现代的做法。如果你在写新代码,建议你按照文档的建议,始终使用tornado.gen.coroutine
。
如果你看看这两个函数的代码(去掉文档部分),就会发现它们的代码结构非常相似。
engine:
def engine(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
runner = None
def handle_exception(typ, value, tb):
if runner is not None:
return runner.handle_exception(typ, value, tb)
return False
with ExceptionStackContext(handle_exception) as deactivate:
try:
result = func(*args, **kwargs)
except (Return, StopIteration) as e:
result = getattr(e, 'value', None)
else:
if isinstance(result, types.GeneratorType):
def final_callback(value):
if value is not None:
raise ReturnValueIgnoredError(
"@gen.engine functions cannot return values: "
"%r" % (value,))
assert value is None
deactivate()
runner = Runner(result, final_callback)
runner.run()
return
if result is not None:
raise ReturnValueIgnoredError(
"@gen.engine functions cannot return values: %r" %
(result,))
deactivate()
# no yield, so we're done
return wrapper
coroutine:
def coroutine(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
runner = None
future = TracebackFuture()
if 'callback' in kwargs:
callback = kwargs.pop('callback')
IOLoop.current().add_future(
future, lambda future: callback(future.result()))
def handle_exception(typ, value, tb):
try:
if runner is not None and runner.handle_exception(typ, value, tb):
return True
except Exception:
typ, value, tb = sys.exc_info()
future.set_exc_info((typ, value, tb))
return True
with ExceptionStackContext(handle_exception) as deactivate:
try:
result = func(*args, **kwargs)
except (Return, StopIteration) as e:
result = getattr(e, 'value', None)
except Exception:
deactivate()
future.set_exc_info(sys.exc_info())
return future
else:
if isinstance(result, types.GeneratorType):
def final_callback(value):
deactivate()
future.set_result(value)
runner = Runner(result, final_callback)
runner.run()
return future
deactivate()
future.set_result(result)
return future
return wrapper
这两个代码一开始可能看起来都挺难懂的。但是很明显,代码是非常相似的,唯一的不同是@gen.coroutine
对callback
这个参数有特别的处理,并且它会构建并返回一个Future
对象。而@gen.engine
的代码则会在你试图返回某个值时抛出错误,而不是把它放进Future
里。