tornado.gen.engine 与 tornado.gen.coroutine 的区别

7 投票
1 回答
2417 浏览
提问于 2025-04-18 05:35

根据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.coroutinecallback这个参数有特别的处理,并且它会构建并返回一个Future对象。而@gen.engine的代码则会在你试图返回某个值时抛出错误,而不是把它放进Future里。

撰写回答