Home > database >  `asyncio.wait` confusion when passed a coroutine
`asyncio.wait` confusion when passed a coroutine

Time:11-09

asyncio.wait says:

Passing coroutines objects to wait() directly is deprecated as it leads to confusing behavior.

and gives this as the confusing behavior:

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: [I modified the code snippet below so that it runs]

async def foo():
    return 42

async def main():
    coro = foo()
    done, pending = await asyncio.wait({coro})

    if coro in done:
        # This branch will never be run!
        print('yay!')

asyncio.run(main())

I just started learning about asyncio, so I don't fully understand. Can someone explain?

CodePudding user response:

The example code that is given in the documentation is:

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

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

The reason that this code gives unexpected results is as follows:

  • coro is a coroutine object.
  • When it is passed to asyncio.wait, it automatically creates a Task object from it (which is different from the coroutine object), like coro_task = create_task(coro) (see create_task).
  • When asyncio.wait is done, it returns two sets:
    • a set of tasks that are already done
    • a set of tasks that are not done yet

So in this case, it will return one set that contains coro_task and one set that is empty.

Note that the original coroutine object coro (which is different from coro_task) is not contained in any of the sets, so checking if it is in the "done" set, is pointless - it will never be contained, even if the corresponding task, coro_task is already done.

The fix is to create the Task object for coro outside of asyncio.wait, which will allow to test if that same object is contained in one or the other of the returned sets, in order to determine if the task is done.

In Python versions starting from 3.8 you will get a deprecation warning if you pass a coroutine to asyncio.wait, and since version 3.11 it will be an error, i.e. you are forced to use the "fixed" code.

  • Related