Tornado 基于生成器的协程
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
。