Python中如何在非async函数内等待协程?
在已运行的异步事件循环中,从同步函数等待可等待对象的解决方案
先看这个简化示例,直接在非异步函数里用await会触发语法错误:
import asyncio async def main(): print(non_async_function()) def non_async_function(): # 语法错误:await不能在非async函数中使用 return await async_function() async def async_function(): return "foo" if __name__ == '__main__': asyncio.run(main())
我们需要在非异步函数内等待可等待对象,但常用方法都会触发报错:
- 用
asyncio.run或asyncio.get_running_loop().run_until_complete会抛出RuntimeError:This event loop is already running - 用
asgiref.async_to_sync会抛出RuntimeError:You cannot use AsyncToSync in the same thread as an async event loop - just await the async function directly
这个问题常出现在链式装饰器场景:部分装饰器会通过inspect.iscoroutinefunction提供异步、同步两种变体,但如果某个装饰器没做这个处理,后续装饰器会误把包装后的函数当成同步函数。对于这类同步装饰器,可以通过检查函数返回值来处理(假设需要先处理结果再返回):
result = func(*args, **kwargs) if inspect.isawaitable(result): return sync_await(result)
实现支持当前异步上下文的sync_await
要实现能复用当前异步上下文及上下文变量的sync_await,可以借助上下文变量复制和事件循环的线程安全提交接口,核心是把可等待对象的执行插入到当前运行的事件循环中,同时保留上下文:
import asyncio import inspect from contextvars import copy_context def sync_await(awaitable): loop = asyncio.get_running_loop() # 复制当前所有上下文变量,确保异步任务能访问到相同的上下文 current_ctx = copy_context() async def run_with_context(): # 恢复上下文后执行可等待对象 return await current_ctx.run(lambda: awaitable) # 向当前运行的事件循环提交协程任务,同步等待结果 future = asyncio.run_coroutine_threadsafe(run_with_context(), loop) return future.result()
测试修改后的示例
把之前的示例改成使用这个sync_await,运行后就能正常输出结果:
import asyncio import inspect from contextvars import copy_context def sync_await(awaitable): loop = asyncio.get_running_loop() current_ctx = copy_context() async def run_with_context(): return await current_ctx.run(lambda: awaitable) future = asyncio.run_coroutine_threadsafe(run_with_context(), loop) return future.result() async def main(): print(non_async_function()) def non_async_function(): result = async_function() if inspect.isawaitable(result): return sync_await(result) return result async def async_function(): return "foo" if __name__ == '__main__': asyncio.run(main())
这段代码运行后会输出foo,既不会触发语法错误,也不会抛出RuntimeError,同时能正确保留当前的上下文变量。
内容的提问来源于stack exchange,提问作者Kiran Jonnalagadda
相关产品推荐
相关产品推荐

