这里的秘密武器是
asyncio
模块。你的
something
对象必须本身就是一个可等待对象,要么依赖于更多可等待对象,要么必须从
Future
对象中产生yield。例如,
asyncio.sleep()
协程会产生一个
Future
:
@coroutine
def sleep(delay, result=None, *, loop=None):
"""Coroutine that completes after a given time (in seconds)."""
if delay == 0:
yield
return result
if loop is None:
loop = events.get_event_loop()
future = loop.create_future()
h = future._loop.call_later(delay,
futures._set_result_unless_cancelled,
future, result)
try:
return (yield from future)
finally:
h.cancel()
这里的语法使用了旧的生成器语法,以保持向后兼容旧版Python 3。
请注意,future不使用await
或yield from
;它们只是使用yield self
直到满足某些条件。在上面的async.sleep()
协程中,当产生结果时(在上面的async.sleep()
代码中,通过futures._set_result_unless_cancelled()
函数调用延迟后),就会满足该条件。
然后,事件循环将继续从它管理的每个挂起的future中获取下一个“结果”(有效地轮询它们),直到未来发出完成信号(通过引发包含结果的StopIteration
异常;例如,从协程中return
就会这样做)。此时,产生该future的协程可以被通知继续(通过发送future结果或如果未来引发除StopIteration
之外的任何异常,则抛出异常)。
因此,以您的示例为例,循环将启动您的
gen()
协程,然后
await something
(直接或间接)产生一个future。该future将被轮询,直到引发
StopIteration
(表示它已完成)或引发其他异常为止。如果future已完成,则执行
coroutine.send(result)
,使其能够继续前进到
return 42
行,触发具有该值的新
StopIteration
异常,从而允许等待
gen()
的调用协程继续等待。
Future
(假设我们选择使用asyncio
)?而这个Future
是在递归结尾处,知道如何与异步框架连接起来?所以生成器协程实际上根本不是异步的? - Paulasync def
协程,那么这是您自己的问题。 :-) - Martijn Pietersasync
或await
语法,但有Futures
。C#和JS具有相同的协程特性,并且也基于futures和事件循环的异步协作的整个前提。 - Martijn Pietersasyncio
负责在协程需要等待时不阻塞,但实际上这是协程的责任。此外,我没有意识到协程最终需要回到事件循环以保持异步。我总是把示例代码中的asyncio.sleep
行视为纯粹用于演示目的,但错过了它实际上与事件循环紧密相关的重点。现在我明白了。我想。 - Paul