You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 03:20:02