Tornado 基于生成器的协程

2022-03-10 14:16 更新

tornado.gen 实现了基于生成器的协程。

注意:

本模块中的“装饰器和生成器”方法是 Python 3.5 中引入的原生协程(使用 ​async def​ 和 ​await​)的先驱。 不需要与旧版本 Python 兼容的应用程序应该使用本机协程。 该模块的某些部分仍然适用于原生协程,特别是 ​multi​、​sleep​、​WaitIterator和 ​with_timeout​。 其中一些函数在 ​asyncio模块中有对应的函数,它们也可以使用,尽管这两个函数不一定是 100% 兼容的。

与链接回调相比,协程提供了一种在异步环境中工作的更简单的方法。 使用协程的代码在技术上是异步的,但它被编写为单个生成器,而不是单独函数的集合。

例如,这是一个基于协程的处理程序:

class GenAsyncHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        http_client = AsyncHTTPClient()
        response = yield http_client.fetch("http://example.com")
        do_something_with_response(response)
        self.render("template.html")

Tornado 中的异步函数返回一个 ​Awaitable或 ​Future​; 产生这个对象会返回它的结果。

您还可以产出其他可缩性对象的列表或字典,它们将同时启动并并行运行; 全部完成后将返回结果列表或字典:

@gen.coroutine
def get(self):
    http_client = AsyncHTTPClient()
    response1, response2 = yield [http_client.fetch(url1),
                                  http_client.fetch(url2)]
    response_dict = yield dict(response3=http_client.fetch(url3),
                               response4=http_client.fetch(url4))
    response3 = response_dict['response3']
    response4 = response_dict['response4']

如果导入了 ​tornado.platform.twisted​,也可以生成 ​Twisted的 ​Deferred对象。

在 3.2 版更改:添加了字典支持。

在 4.1 版更改: 添加了对通过 ​singledispatch生成 ​asyncio Futures​ 和 ​Twisted Deferreds​ 的支持。

装饰器

tornado.gen.coroutine(func: Union[Callable[[...], Generator[Any, Any, _T]], Callable[[...], _T]]) → Callable[[...], Future[_T]]

异步生成器的装饰器。

为了与旧版本的 Python 兼容,协程也可以通过引发特殊异常 ​Return(value​) 来“​return​”。

带有这个装饰器的函数返回一个 ​Future​。

当协程内部发生异常时,异常信息将存储在 ​Future对象中。 您必须检查 ​Future对象的结果,否则您的代码可能不会注意到异常。 这意味着如果从另一个协程调用时产生函数,使用 ​IOLoop.run_sync​ 之类的东西进行顶级调用,或者将 ​Future传递给 ​IOLoop.add_future​。

在 6.0 版更改: ​callback​参数已删除。 请改用返回的可等待对象。

exception tornado.gen.Return(value: Any = None)

从协程返回值的特殊异常。

如果引发此异常,则将其 ​value参数用作协程的结果:

@gen.coroutine
def fetch_json(url):
    response = yield AsyncHTTPClient().fetch(url)
    raise gen.Return(json_decode(response.body))

在 Python 3.3 中,不再需要这个异常:​return语句可以直接用于返回一个值(以前 ​yield和 ​return不能在同一个函数中组合使用)。

与 ​return语句类比, ​value参数是可选的,但从来没有必要引发 ​gen.Return()​。 ​return语句可以不带参数使用。

实用功能

tornado.gen.with_timeout(timeout: Union[float, datetime.timedelta], future: Yieldable, quiet_exceptions: Union[Type[Exception], Tuple[Type[Exception], ...]] = ())

在​timeout​中包装 ​Future​(或其他可产生的对象)。

如果输入​future​在​timeout​之前未完成,则引发 ​tornado.util.TimeoutError​,这可以以 ​IOLoop.add_timeout允许的任何形式指定(即 ​datetime.timedelta​ 或相对于 ​IOLoop.time​ 的绝对时间)

如果包装的 ​Future在超时后失败,则将记录异常,除非它是 ​quiet_exceptions​ 中包含的类型(可能是异常类型或类型序列)或 ​asyncio.CancelledError​。

当​timeout​到期时,包装的 ​Future不会被取消,允许它被重用。 ​asyncio.wait_for​ 与此函数类似,但它会在​timeout​时取消包装的 ​Future​。

在 4.1 版更改: 添加了 ​quiet_exceptions 参数和未处理异常的日志记录。

在 4.4 版更改: 添加了对 ​Future 以外的可缩性对象的支持。

在 6.0.3 版更改: ​asyncio.CancelledError​ 现在总是被认为是“​quiet​”。

tornado.gen.sleep(duration: float) → Future[None]

返回在给定秒数后解析的 ​Future​。

当在协程中与 ​yield一起使用时,这是 ​time.sleep​ 的非阻塞模拟(不应在协程中使用,因为它是阻塞的):

yield gen.sleep(0.5)

请注意,单独调用此函数不会执行任何操作; 你必须等待它返回的 ​Future(通常是通过​yielding it​)。

class tornado.gen.WaitIterator(*args, **kwargs)

提供一个迭代器以在等待对象完成时产生结果。

产生一组像这样的等待对象:

results = yield [awaitable1, awaitable2]

暂停协程,直到 ​awaitable1和 ​awaitable2都返回,然后使用两个 ​awaitables的结果重新启动协程。 如果任一 ​awaitable引发异常,则表达式将引发该异常并且所有结果都将丢失。

如果你需要尽快得到每个 ​awaitable的结果,或者如果你需要一些 ​awaitable的结果,即使是其他的产生错误,你可以使用 ​WaitIterator

wait_iterator = gen.WaitIterator(awaitable1, awaitable2)
while not wait_iterator.done():
    try:
        result = yield wait_iterator.next()
    except Exception as e:
        print("Error {} from {}".format(e, wait_iterator.current_future))
    else:
        print("Result {} received from {} at {}".format(
            result, wait_iterator.current_future,
            wait_iterator.current_index))

因为结果一旦可用就会返回,迭代器的输出与输入参数的顺序不同。 如果您需要知道哪个 ​future产生了当前结果,您可以使用属性 ​WaitIterator.current_future​ 或 ​WaitIterator.current_index​ 从输入列表中获取 ​awaitable的索引。 (如果在 ​WaitIterator的构造中使用了关键字参数,​current_index将使用相应的关键字)。

在 Python 3.5 上,​WaitIterator实现了异步迭代器协议,因此它可以与 ​async for 语句一起使用(请注意,在此版本中,如果任何值引发异常,整个迭代都会中止,而前面的示例可以继续过去个别错误):

async for result in gen.WaitIterator(future1, future2):
    print("Result {} received from {} at {}".format(
        result, wait_iterator.current_future,
        wait_iterator.current_index))

done() → bool

如果此迭代器没有更多结果,则返回 ​True​。

next() → _asyncio.Future

返回将产生下一个可用结果的 ​Future​。

请注意,此 ​Future与任何输入都不是同一个对象。

tornado.gen.multi(Union[List[Yieldable], Dict[Any, Yieldable]], quiet_exceptions: Union[Type[Exception], Tuple[Type[Exception], ...]] = ())

并行运行多个异步操作。

children可以是一个列表,也可以是一个字典,其值是可产生的对象。​multi()​ 返回一个新的 ​yieldable对象,该对象解析为包含其结果的并行结构。 如果 ​children是一个列表,则结果是一个相同顺序的结果列表; 如果是字典,则结果是具有相同键的字典。

也就是说,​results = yield multi(list_of_futures)​ 等价于:

results = []
for future in list_of_futures:
    results.append(yield future)

如果任何children​引发异常, ​multi()​ 将引发第一个异常。 所有其他人都将被记录,除非它们属于 ​quiet_exceptions​ 参数中包含的类型。

在基于 yield 的协程中,通常不需要直接调用此函数,因为协程运行程序会在产生列表或字典时自动执行此操作。 但是,在基于 ​await的协程中是必需的,或者传递 ​quiet_exceptions 参数。

由于历史原因,此函数在名称 ​multi()​ 和 ​Multi()​ 下可用。

取消 ​multi()​ 返回的 ​Future不会取消其子级。 ​asyncio.gather 类似于 ​multi()​,但它确实取消了它的​children​。

在 4.2 版更改: 如果多个 ​yieldable失败,将记录第一个之后的任何异常(引发)。 添加了 ​quiet_exceptions参数以抑制所选异常类型的此日志记录。

在 4.3 版更改: 用统一的函数 ​multi替换了类 ​Multi和函数 ​multi_future​。 添加了对 ​YieldPoint和 ​Future以外的支持。

tornado.gen.multi_future(Union[List[Yieldable], Dict[Any, Yieldable]], quiet_exceptions: Union[Type[Exception], Tuple[Type[Exception], ...]] = ())

并行等待多个异步​future​。

从 Tornado 6.0 开始,这个功能和 ​multi完全一样。

4.0 版中的新功能。

在 4.2 版更改: 如果多个 ​Futures失败,将记录第一个(引发)之后的任何异常。 添加了 ​quiet_exceptions参数以抑制所选异常类型的此日志记录。

4.3 版后已弃用:改用 ​multi

tornado.gen.convert_yielded(yielded: Union[None, Awaitable[T_co], List[Awaitable[T_co]], Dict[Any, Awaitable[T_co]], concurrent.futures._base.Future]) → _asyncio.Future

将产生的对象转换为 ​Future

默认实现接受列表、字典和Futures。 这具有启动任何没有自己启动的协程的副作用,类似于 ​asyncio.ensure_future​。

如果可以使用 ​singledispatch库,则可以扩展此功能以支持其他类型。 例如:

@convert_yielded.register(asyncio.Future)
def _(asyncio_future):
    return tornado.platform.asyncio.to_tornado_future(asyncio_future)

tornado.gen.maybe_future(x: Any) → _asyncio.Future

将 x 转换为 Future。

如果 x 已经是 Future,则简单地返回它; 否则它会被包裹在一个新的 Future 中。 当您不知道 f() 是否返回 Future 时,这适合用作 result = yield gen.maybe_future(f()) 。

4.3 版后已弃用:此函数仅处理 ​Futures​,而不处理其他可生成对象。 不是 ​may_future​,而是检查您期望的非​future​结果类型(通常只是 ​None​),并产生任何未知的结果。

tornado.gen.is_coroutine_function(func: Any) → bool

返回 func 是否为协程函数,即用​coroutine​包裹的函数。

tornado.gen.moment

一个特殊的对象,可以让 ​IOLoop ​运行一次迭代。

这在正常使用中是不需要的,但它对长时间运行的协程很有帮助,这些协程可能会产生立即准备好的 Futures。

用法:​yield gen.moment

在原生协程中,​yield gen.moment​ 等同于 ​await asyncio.sleep(0)​。

4.0 版中的新功能。

4.5 版后已弃用:​yield None​(或没有参数的 ​yield​)现在等同于 ​yield gen.moment​。


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号