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 aTask
object from it (which is different from the coroutine object), likecoro_task = create_task(coro)
(seecreate_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.