【问题标题】:How to call async function in sync code and break async/await chain (i.e. how to wrap an async function in a sync function)如何在同步代码中调用异步函数并中断异步/等待链(即如何将异步函数包装在同步函数中)
【发布时间】:2022-04-22 05:43:54
【问题描述】:

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

由于func_1 是异步的,我需要等待它,但由于我在func_2 中调用它,我还需要使func_2 异步(我不能在非异步函数中等待)。这继续下去;我需要将整个函数链func_2func_3func_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

【问题讨论】:

  • 您可以在func_1_wrapper 中执行func_1 并使用asyncio.run 等待它完成:asyncio.run(func_1())
  • 如果你关心它是异步的,因为它允许(伪)并行代码执行,那么是的,你需要把awaitasync一直做向上。如果没有,您可以使用func_1 阻止所有内容直到完成,只需asyncio.run(func_1())
  • 在旧版本中你会使用asyncio.get_event_loop().run_until_complete(func_1())
  • @deceze 我之前试过,但我得到了RuntimeError: This event loop is already running。它仅在函数内部运行时才有效吗? 更新:当我直接在 jupyter notebook 中运行它时确实会弹出错误,但当我在函数中运行它时不会。现在就试试,谢谢你的帮助。
  • 对那些遇到我的问题的人的权利:RuntimeError 发生是因为 Ipython 运行自己的事件循环。要绕过它,您还需要 import nest_asyncio 并运行 nest_asyncio.apply() before asyncio.get_event_loop().run_until_complete(func_1(..))。见:github.com/microsoft/playwright-python/issues/178

标签: python async-await python-asyncio


【解决方案1】:

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

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

处理这些极端情况的一种简单方法是使用像asgiref.sync 这样的库,它允许您执行以下操作:func_1_sync = async_to_sync(func_1),然后直接从同步函数中调用func_1_sync()。这是他们 pypi 页面中的一个 sn-p:

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

特别是:

AsyncToSync 让同步子线程在异步执行时停止并等待 在主线程的事件循环上调用函数,然后控制 当异步函数完成时返回给线程。 SyncToAsync 让异步代码调用同步函数,该函数运行 在线程池中,当 同步功能完成。这个想法是为了更容易打电话 来自异步代码的同步 API 和来自 同步代码,因此更容易将代码从一种样式转换为 另一个。

另一个库是syncer

有时(主要是在测试中)我们需要转换异步函数 到正常的同步函数并同步运行它们。有可能 由 ayncio.get_event_loop().run_until_complete() 完成,但相当 长……

Syncer 使这种转换变得容易。

  • 将协程函数(由 aync def 定义)转换为普通(同步)函数
  • 同步运行协程
  • 同时支持异步 def 和装饰器 (@asyncio.coroutine) 样式

【讨论】:

    猜你喜欢
    • 2020-12-20
    • 2022-07-06
    • 2017-06-18
    • 2020-10-20
    • 2012-07-25
    • 1970-01-01
    • 2019-01-16
    • 2020-03-09
    • 2021-08-28
    相关资源
    最近更新 更多