在每个异步任务完成后立即处理任务列表

3

当一个async任务完成时,我该如何立即处理任务结果呢?

例如,以下代码应该会显示最先加载的页面:

urls = ['stackoverflow.com', 'google.com']
tasks = [asyncio.create_task(fetch_page(x)) for x in urls]

for page in asyncio.give_me_results_ASAP(tasks):
    print(page.url)

由于谷歌加载速度更快,我希望它能打印:

google.com
stackoverflow.com

1
也许最简单的方法是将打印语句添加到获取页面的内部? - Brown Bear
1
你能更详细地解释一下你的优先事项吗?对你来说,顺序是最重要的还是紧急性是最重要的?如果紧急性是主要目标,为什么它是目标?即使它们牺牲了排序,你是否愿意接受能更快完成工作的解决方案? - user2357112
@BearBrown 这是个好点子。虽然我想到了某种异步迭代器。(这样,我可以以线程安全的方式tee出结果,但也可以立即处理它们。) - Mateen Ulhaq
@user2357112 立即性。顺序无关紧要。 - Mateen Ulhaq
1个回答

3

asyncio.as_completed 的设计就是为了解决这个问题,并按照任务完成的顺序返回协程的迭代器。从迭代器中返回的第一个协程将对应于完成的第一个任务,您可以在每个协程上使用 await 来获取任务的结果。

# With Python 3.8+
import asyncio
import time

async def fetch_page(url):
    reponse_time = 0.1 if url == 'google.com' else 0.8
    await asyncio.sleep(reponse_time)
    return url

async def main():
    urls = ['stackoverflow.com', 'google.com']
    tasks = [asyncio.create_task(fetch_page(x)) for x in urls]

    for coro in asyncio.as_completed(tasks):
        print(f"{time.time():.3f}", await coro)

if __name__ == '__main__':
    asyncio.run(main())

生成:

1600821288.178 google.com
1600821288.280 stackoverflow.com

另外值得关注的是:CPython实现asyncio.as_completed - Mateen Ulhaq

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