我对Javascript中的承诺有一点经验。我对Python非常有经验,但对于它的协同程序来说是新手,有一点我无法理解:异步性在哪里开始?
让我们考虑以下最小例子:
async def gen():
await something
return 42
据我所知,await something
将我们的函数执行放在一边,让主程序运行其他位。在某些时候something
会有一个新结果,gen
很快就会有结果。
如果gen
和something
是协程,那么通过所有互联网智慧,它们都是生成器。通过轮询来了解生成器何时可用新项目的唯一方法是:x=gen(); next(x)
。但这是阻止!当x
有结果时,调度程序如何“知道”?答案不能是“当something
有结果时”,因为something
也必须是生成器(因为它是一个协程)。这个论点递归地适用。
我无法理解这个想法,在某些时候,这个过程只需要坐下来等待。
答案 0 :(得分:6)
这里的秘诀是asyncio
module。你的something
对象本身必须是一个等待的对象,要么依赖于更多等待的对象,要么必须从Future
object得到。
例如,asyncio.sleep()
coroutine会产生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版本。)
请注意,未来不会使用await
或yield from
;他们只使用yield self
until some condition is met。在上面的async.sleep()
协程中,在生成结果时(在上面的async.sleep()
代码中,通过延迟后调用的futures._set_result_unless_cancelled()
函数)满足该条件。
事件循环然后继续从它管理的每个待处理的未来(有效地轮询它们)中拉入下一个“结果”,直到将来发出信号(通过引发保持结果的StopIteration
异常; {{1例如,从一个协同例程就可以做到这一点。在那时,产生未来的协程可以继续发出信号(通过发送未来结果,或者如果未来提出除return
以外的任何内容,则抛出异常)。
因此,对于您的示例,循环将启动您的StopIteration
协程,然后gen()
(直接或间接)产生未来。对该未来进行轮询,直到它引发await something
(发出信号)或引发其他一些异常。如果未来完成,则执行StopIteration
,然后允许它前进到coroutine.send(result)
行,触发带有该值的新return 42
异常,允许等待{{1}的呼叫协程继续等等。