如何在同步代码中调用异步函数并打破异步/等待链(即如何将异步函数包装在同步函数中)

Mar*_*ses 12 python async-await python-asyncio

我所有的代码都是在没有考虑 asyncio 的情况下编写的;但是,我使用一个异步函数(由另一位开发人员编写;出于我的目的,它是一个黑匣子)。我们就这样称呼吧func_1。我需要从另一个函数中调用这个函数,调用它func_2(它本身可以在任意长的函数链中调用func_3func_4等等......)。

由于func_1是异步的,我需要等待它,但由于我在 中调用它func_2,所以我也需要进行func_2异步(我不能在非异步函数中等待)。这样的情况一直持续下去;我需要将整个函数链func_2, func_3,转换func_4为异步函数。

有办法避免这种情况吗?我只想调用func_1,等待它完成,然后在我的正常 python 代码的其余部分中使用结果。我可以创建一个包装器func_1来允许这样做吗?

我想要的基本上是以下内容,但不起作用:

# This is the function defined by someone else
async def func_1(*args):
    return something(*args)

# This is my wrapper
def func_1_wrapper(*args):
    return await func_1(*args)

# So that I can call it like normal within the rest of my code
def func_2(*args):
    # do something
    a = func_1_wrapper(*args)
    # do something else
Run Code Online (Sandbox Code Playgroud)

小智 4

本质上,您想要启动一个事件循环(异步代码的“引擎”),将函数提交到事件循环,然后等待事件循环执行该函数。

\n

一种方法是asyncio.run(func_1()),但如果代码中的其他内容已经启动了事件循环,或者如果您在多线程上下文中运行它,则可能会遇到问题。

\n

处理这些边缘情况的一个简单方法是使用一个类似的库,asgiref.sync它允许您执行以下操作:func_1_sync = async_to_sync(func_1)然后func_1_sync()直接从同步函数调用。这是他们的 pypi 页面的片段:

\n
\n

这些[辅助函数]允许您包装或装饰异步或同步函数,以便从其他样式调用它们(因此您可以从同步线程调用异步函数,反之亦然)。

\n

尤其:

\n

当在主线程\xe2\x80\x99s 事件循环上调用异步函数时,AsyncToSync 让同步子线程停止并等待,然后在异步函数完成时将控制返回给线程。\nSyncToAsync 让异步代码调用同步函数,在线程池中运行,并在同步函数完成时将控制权返回给异步协程。其想法是让从异步代码调用同步 API 和从同步代码调用异步 API 变得更容易,以便更轻松地将代码从一种样式转换为另一种样式。

\n
\n

另一个执行此操作的库是syncer

\n
\n

有时(主要是在测试中)我们需要将异步函数转换为正常的同步函数并同步运行它们。它可以通过 ayncio.get_event_loop().run_until_complete() 完成,但它\xe2\x80\x99s 相当\nlong\xe2\x80\xa6

\n

Syncer 使这种转换变得容易。

\n
    \n
  • 将协程函数(由 aync def 定义)转换为普通(同步)函数
  • \n
  • 同步运行协程
  • \n
  • 支持 async def 和装饰器 (@asyncio.coroutine) 风格
  • \n
\n
\n