想象一下以下非常常见的情况:您编写了一个冗长而复杂的函数,并意识到应该将一些代码提取到单独的函数中以便重用和/或可读。通常,这个额外的函数调用不会改变程序的语义。
但是,现在假设您的函数是一个协程,并且您要提取的代码包含至少一个异步调用。将它提取到一个单独的函数现在会突然改变你的程序语义,方法是插入一个新的协同程序产生的点,事件循环控制,并且可以在其间安排任何其他的协程。
之前的例子:
async def complicated_func():
foo()
bar()
await baz()
之后的例子:
async def complicated_func():
foo()
await extracted_func()
async def extracted_func():
bar()
await baz()
在之前的示例中,complicated_func
保证在调用foo()
和调用bar()
之间不会被暂停。重构后,这种保证就会丢失。
我的问题是:是否可以调用extracted_func()
使其立即执行,就像它的代码是内联的一样?或者是否有其他方法可以在不改变程序语义的情况下执行此类常见的重构任务?
答案 0 :(得分:7)
重构后,此保证将丢失。
实际上并非如此。
是否可以调用extracted_func()使其立即执行,就好像它的代码是内联的一样?
情况已经如此。
await some_coroutine()
表示some_coroutine
可能会控制回事件循环,但在实际等待未来之前它不会这样做(例如某些I / O操作)。 / p>
考虑这个例子:
import asyncio
async def coro():
print(1)
await asyncio.sleep(0)
print(3)
async def main():
loop.call_soon(print, 2)
await coro()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
请注意2
和1
之间如何按预期打印3
。
这也意味着可以通过编写这样的代码来冻结事件循环:
async def coro():
return
async def main():
while True:
await coro()
在这种情况下,事件循环永远不会有机会运行另一个任务。