如何检查 asyncio.wait 后完成的协程

How to check what coroutine is completed after asyncio.wait

考虑以下代码:

import random
import asyncio

class RandomLife(object):
    def __init__(self, name: str):
        self.name = name
        self.coro = asyncio.sleep(random.randrange(0, 5))

    def __await__(self):
        return self.coro.__await__()


async def main():
    objects = [RandomLife("one"), RandomLife("two"), RandomLife("three")]

    finished, unfinished = await asyncio.wait(objects, return_when=asyncio.FIRST_COMPLETED)

    print(finished)

    await asyncio.wait(unfinished)

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

然后首先 asyncio.wait 我想知道 RandomLife 的哪个实例已完成。但是 finished 变量是一组 Task s,而不是 RandomLife 实例。如何将此任务转换为 RandomLife?可能吗?

the documentation 警告:

Note wait() schedules coroutines as Tasks automatically and later returns those implicitly created Task objects in (done, pending) sets. Therefore the following code won’t work as expected:

async def foo():
    return 42

coro = foo()
done, pending = await asyncio.wait({coro})

if coro in done:
    # This branch will never be run!

Here is how the above snippet can be fixed:

async def foo():
    return 42

task = asyncio.create_task(foo())
done, pending = await asyncio.wait({task})

if task in done:
    # Everything will work as expected now.

我们可以使用相同的技巧。首先,我们需要将所有协程包装到任务中,然后设置从创建的任务到其 RandomLife 实例的映射:

import random
import asyncio

class RandomLife(object):
    def __init__(self, name: str):
        self.name = name
        self.coro = asyncio.sleep(random.randrange(0, 5))

    def __await__(self):
        return self.coro.__await__()


async def main():
    objects = [RandomLife("one"), RandomLife("two"), RandomLife("three")]

    # Wrap all the coros to tasks, as the documentation suggests.
    tasks = [asyncio.create_task(o.coro) for o in objects]
    # Set up mapping from tasks created to RandomLife instances.
    task2life = dict(zip(tasks, objects))

    finished, unfinished = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)

    # Get first task finished.
    finised_task = list(finished)[0]
    # Map it back to the RandomLife instance.
    finished_life = task2life[finised_task]

    print(finished_life.name)

    await asyncio.wait(unfinished)

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