Python-asyncio类型错误:对象字典无法在“await”表达式中使用。

77

我正在使用一个第三方模块从API检索数据。我只想异步等待该模块返回数据,这可能需要几秒钟,并且会冻结我的应用程序。但是,当我尝试等待对该模块的调用时,我收到TypeError:

TypeError:对象字典不能在'await'表达式中使用

import thirdPartyAPIwrapper

async def getData():
    retrienveData = await thirdPartyAPIWrapper.data()
    return await retrieveData

def main():
    loop = asncio.get_event_loop()
    data = loop.run_until_complete(getData())
    loop.close
    return data

为什么我不能等待一个类型为 'dict' 的对象?有没有其他办法可以解决这个问题? 如果使用async/await和asyncio与不返回协程的第三方模块无法正常工作,那么我还有哪些选择?


1
是的,在查看该模块的源代码时,它是一个普通的“def”函数。 - Riley Hughes
4个回答

84

只有异步函数(使用 async def 定义)才能被等待。整个思路是这样的,这种函数的编写方式特殊,可以在不阻塞事件循环的情况下运行(await)。

如果您想从一个普通的函数(使用 def 定义)中获取需要执行相当长时间的结果,则有以下选项:

  • 将整个函数重写为异步函数
  • 在另一个线程中调用此函数并异步等待结果
  • 在另一个进程中调用此函数并异步等待结果

通常情况下,您会选择第二个选项。

以下是如何操作的示例:

import asyncio
import time
from concurrent.futures import ThreadPoolExecutor


_executor = ThreadPoolExecutor(1)


def sync_blocking():
    time.sleep(2)


async def hello_world():
    # run blocking function in another thread,
    # and wait for it's result:
    await loop.run_in_executor(_executor, sync_blocking)


loop = asyncio.get_event_loop()
loop.run_until_complete(hello_world())
loop.close()

请阅读这篇回答,了解asyncio的工作方式。我认为它会对你很有帮助。


6
请注意,与 JavaScript 不同,Python 并非以异步编程为中心设计的语言。在 Python 中,如果要执行异步操作,你需要明确地定义异步函数,普通函数不能自动变成异步函数。这就是为什么你需要使用线程来将同步函数转换为异步函数的原因。 - Mikhail Gerasimov

8

您不需要等待正在异步运行的函数完成

async def getData():
    retrienveData = thirdPartyAPIWrapper.data()
    return retrieveData

8
thirdPartyAPIWrapper.data() 是一个普通的同步函数,所以应该在另一个线程中调用它。
asgiref 库中有一个帮助函数可以实现这一点。
假设我们有一个带有参数的阻塞函数:
import asyncio
import time

from asgiref.sync import sync_to_async


def blocking_function(seconds: int) -> str:
    time.sleep(seconds)
    return f"Finished in {seconds} seconds"

async def main():
    seconds_to_sleep = 5
    function_message = await sync_to_async(blocking_function)(seconds_to_sleep)
    print(function_message)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

该库还提供了一个async_to_sync辅助函数。


0

我正在编写测试案例,需要模拟异步功能。因此,您可以编写一个简单的辅助函数,如下所示。

async def resolve(val):
    return val

现在你可以等待任何东西了

foo = resolve(1) 
await foo # No error!

不涉及来自第三方的长时间同步进程。 - pixelpax

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接