首页 > 解决方案 > 在一个异步函数返回 True 后终止事件循环

问题描述

我有一个函数可以在不同的网页上查找一些信息,如果在目标网页上找到它,则返回 True,否则返回 False。我想多次调用这个函数,每次针对不同的网页,我希望它们中的每一个都异步运行,并且我希望整个过程无限循环,直到该函数的一个实例返回 True。整个代码很长,所以我将在下面对其进行简化,以便理解这一点。这段代码可以工作并且可以满足我的要求,但我想知道是否有更好或更清洁的方法来实现相同的目标。我对 asyncio 很陌生,所以请放轻松:)

import asyncio


async def find_something(loop, item_to_find, website):
    while True:
        # Some code to check the contents of a webpage goes here
        if item_to_find in website:
            loop.stop()
            return True
        else:
            return False

loop = asyncio.get_event_loop()

try:
    asyncio.ensure_future(find_something(loop, item1, website1))
    asyncio.ensure_future(find_something(loop, item2, website2))
    asyncio.ensure_future(find_something(loop, item3, website3))

    loop.run_forever()
    
except Exception as e:
    pass

finally:
    loop.close()

编辑:我在代码中犯了一个错误,因为我只使用 print 语句进行了原始测试,在这种情况下,返回 False 会终止 while 循环并自动结束函数调用,因此函数不会无限循环。因此,需要替代解决方案的更多理由。我可以完全删除while循环并在其内部调用函数以进行递归循环,直到满足条件,或者除非满足所需条件,否则我不能返回任何内容,尽管这些听起来都不是一个好的解决方案

标签: pythonpython-3.xpython-asyncio

解决方案


我相信asyncio.as_completed这就是你要找的。它将在每个等待完成时返回结果。

import asyncio

async def find_something(item_to_find, website):
    contents = await some_code_to_check_the_contents_of_a_webpage(website)
    return item_to_find in contents

async def main():
    aws = [
        find_something(item1, website1),
        find_something(item2, website2),
        find_something(item3, website3),
    ]

    for coro in asyncio.as_completed(aws):
        was_found = await coro
        if was_found:
            return


asyncio.run(main())

需要注意的是,无法知道find_something返回的调用是哪个True。如果这很重要,或者如果您需要取消任何挂起的任务,您可能希望从返回布尔值切换到返回一些可以为您提供所需信息的值(例如,参数)。然后,您可以将期货放在映射中并取消您尚未看到的任何期货。这可能看起来像

aws = {
    (item1, website1): asyncio.ensure_future(item1, website1),
    (item2, website2): asyncio.ensure_future(item2, website2),
    (item3, website3): asyncio.ensure_future(item3, website3),
}

for coro in asyncio.as_completed(aws.values()):
    item, website, was_found = await coro
    del aws[item, website]
    if was_found:
        for future in aws.values():
            future.cancel()
        return

推荐阅读