最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

如何与Python中的Javascript回调类似

运维笔记admin13浏览0评论

如何与Python中的Javascript回调类似

如何与Python中的Javascript回调类似

[我正在努力使自己了解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。您永远不要调用该函数,它会阻塞整个事件循环。改为await asyncio.sleep()。用JavaScript术语来说,调用time.sleep与休眠like this而不是like this几乎一样糟糕。 (我说“几乎”,因为time.sleep至少在等待时不会消耗CPU周期。)

此错误导致您遇到第二个问题,即使用asyncio.sleep(0)来控制排气回路。尽管它是Guido所添加的,但它是针对高级场景的。初学者使用该功能几乎总是一个错误。如果您长时间运行的操作是异步操作-就像您代码中的操作一样(一旦time.sleep()被替换为await asyncio.sleep()),则无需显式删除事件循环,异步操作将自行删除在下一个await上,就像在JavaScript中一样。

在Node.js中,当我们进行外部API调用时,我们得到了一个叫做Promises的东西,我们可以等待完成。

在Python中,未来非常接近,异步模型非常相似。唯一的区别是,在JavaScript中,您可以调用异步函数来创建Promise,并且事件循环将自动安排其运行。在Python中,仅调用异步函数本身并不会执行任何操作,您必须等待它或对其调用asyncio.create_task()。由于您的代码执行了后者,因此它看起来是正确的。

此外,您可能想使用asyncio.gather()而不是asyncio.wait()以确保不会引起异常。如果您使用的是Python 3.7,则可以使用新的asyncio.run()调用来运行异步主函数。

发布评论

评论列表(0)

  1. 暂无评论