Search code examples
pythonasynchronouspython-asyncio

Why is asyncio.Future.done() not set to True when the task is done?


In this example code a asyncio.Future is created and run. However the state is not set to done once it is complete.

import asyncio
from concurrent.futures.thread import ThreadPoolExecutor
from time import sleep

_executor = ThreadPoolExecutor(max_workers=32)

def test():
    print('starting test')
    sleep(5)
    print('ending test')

async def main():
    loop = asyncio.get_running_loop()
    result = loop.run_in_executor(_executor, test)

    sleep(3)
    print('sleep 1 complete')
    print(f'{result.done()=}')

    sleep(3)
    print('sleep 2 complete')
    print(f'{result.done()=}')

    print('await result')
    await result
    print(f'{result.done()=}')

asyncio.run(main())

This results in:

starting test
sleep 1 complete
result.done()=False
ending test
sleep 2 complete
result.done()=False
await result
result.done()=True

Why is result.done() set to False after the second sleep?


Solution

  • You are not yielding the control to the asyncio loop in the first steps in your snippet: the asyncio loop never gets a chance to check the Future status and set whatever in it.

    For one take away, make sure you understand this: no async related code can run unless you pass control to the loop with one of await, async with or async for

    There is no, "with enough sleep, eventually the asyncio will kick in and do its thing". No. It is single threaded code, and one of the benefits of using it is giving the user full control of where the task changes take place. And in your code, they take place just in the second-to-last line with await result.

    Yes, in this case, the task itself is dispatched to another thread in the executor, and it runs there - but it is the asyncio loop which checks for the task completion, set the attributes in the Future, and so on.