gpt4 book ai didi

python - 如何在 Python 中实现类似于 Javascript 回调的功能

转载 作者:行者123 更新时间:2023-12-01 07:10:14 27 4
gpt4 key购买 nike

我正在尝试了解 Python asyncio 。这是我写的一个简单的程序。我试图模拟的逻辑如下:

  1. 我从某个数据库中获取了姓名列表。由于我们将在获得这些名称后对其进行一些操作,因此我将其设为一个简单的函数,而不是异步函数。

  2. 获取数据后,我们再次使用我们拥有的名称调用某些外部 API。现在,由于从 IO 的角度来看,这将是一项昂贵的操作,并且对各个名称的 API 调用并不相互依赖,因此使它们匿名是有意义的。

我在 Stackoverflow( Cooperative yield in asyncio ) 中查找了这个线程,它说要将控制权交还给事件循环来做其他事情,我们必须做 asyncio.sleep(0) .

这里我比较 Node.js 和 Python 的异步行为。如果我使用上述语法将控制权交还给事件循环,我长时间运行的 API 调用将保持暂停状态,并且不会像 Node.js 那样在后台发生?

在 Node.js 中,当我们进行外部 API 调用时,我们会得到一些名为 Promises 的信息,我们可以等待它完成。它本质上意味着数据库调用或 API 调用在后台发生,完成后我们会返回一些内容。

我是否遗漏了一些关于 Python 异步编程的关键概念?请对此提供更多说明。

下面是代码及其输出:

import asyncio
import time


async def get_message_from_api(name):
# This is supposed to be a long running operation like getting data from external API
print(f"Attempting to yield control to the other tasks....for {name}")
await asyncio.sleep(0)
time.sleep(2)
return f"Creating message for {name}"


async def simulate_long_ops(name):
print(f"Long running operation starting for {name}")
message = await get_message_from_api(name)
print(f"The message returned by the long running operation is {message}")


def get_data_from_database():
return ["John", "Mary", "Sansa", "Tyrion"]


async def main():
names = get_data_from_database()
futures = []
for name in names:
futures.append(loop.create_task(simulate_long_ops(name)))
await asyncio.wait(futures)


if __name__ == '__main__':
try:
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
except Exception as e:
print(e)
finally:
loop.close()

输出:

Long running operation starting for John
Attempting to yield control to the other tasks....for John
Long running operation starting for Mary
Attempting to yield control to the other tasks....for Mary
Long running operation starting for Sansa
Attempting to yield control to the other tasks....for Sansa
Long running operation starting for Tyrion
Attempting to yield control to the other tasks....for Tyrion
The message returned by the long running operation is Creating message for John
The message returned by the long running operation is Creating message for Mary
The message returned by the long running operation is Creating message for Sansa
The message returned by the long running operation is Creating message for Tyrion

最佳答案

代码中的错误是您调用了time.sleep。您永远不应该在异步代码中调用该函数,它会阻塞整个事件循环;改为等待 asyncio.sleep() 。用 JavaScript 术语来说,调用 time.sleep 几乎和 sleep 一样糟糕 like this而不是like this 。 (我说“几乎”是因为 time.sleep 至少在等待时不会消耗 CPU 周期。)

尝试解决该错误导致了第二个问题,即使用 asyncio.sleep(0) 来控制事件循环。尽管这个习惯用法很早就被添加了,但该行为的记录却很少later 。作为吉多 hints在最初的问题中,显式屈服于事件循环仅适合高级使用,初学者使用它很可能是一个错误。如果您的长时间运行的操作是异步的 - 就像您的代码中的情况一样,一旦将 time.sleep() 替换为 await asyncio.sleep() - 您就不需要不需要手动放入事件循环。相反,异步操作将根据需要在每次 await 上删除,就像在 JavaScript 中一样。

In Node.js when we make an external API call we get something back called Promises on which we can wait to finish.

在 Python 中 future是一个密切的对应物,并且异步模型非常相似。一个显着的区别是 Python 的异步函数不返回预定的 future,而是返回轻量级协程对象,您必须等待或传递给 asyncio.create_task()让他们跑。由于您的代码执行后者,因此它看起来是正确的。

create_task的返回值为an object实现 Future 接口(interface)。 future 体育add_done_callback具有您期望的语义的方法。但最好只是等待 future - 它使代码更具可读性,并且很清楚异常发生在哪里。

此外,您可能想使用 asyncio.gather()而不是 asyncio.wait() 以确保异常不会被忽视。如果您使用的是 Python 3.7,请考虑使用 asyncio.run()运行异步主函数。

关于python - 如何在 Python 中实现类似于 Javascript 回调的功能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58261157/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com