asyncio create_task to run forever

Question:

I have the following code

import asyncio

loop = asyncio.get_event_loop()

async def while_loop():
    n = 0
    while True:
        print(f"{n}")
        await asyncio.sleep(2)
        n = n+1

async def some_func():
    await asyncio.sleep(5)
    print("Some Func")

future = loop.create_task(while_loop())
loop.run_until_complete(some_func())

I expected the while_loop function to run forever but it seems to only executes as a result of calling run_until_complete and it stops printing the while loop once some_func is finished executing. The output I see is:

0
1
2
Some Func

I expected the numbers to keep printing even after some_func is completed.

0
1
2
Some Func
3
4
5
6
.
.
.

The only way to get more numbers printed is to call some_func again.

Asked By: slaw

||

Answers:

I expected the numbers to keep printing even after some_func is completed.

The argument to run_until_complete controls how long the event loop is run. And once the event loop stops running, all coroutines are effectively suspended, not just the one that you’ve been waiting for. But you do have different options:

  • loop.run_until_complete(some_func()) – what you already used; run the event loop until the some_func coroutine finishes. Executes other coroutines in parallel during that time as well, but also stops executing them as soon as the event loop finishes.

  • loop.run_forever() – run the event loop until some coroutine or callback invokes loop.stop(). If none of them do that, then the event loop will not halt, even if all the coroutines come to an end. In your case you’d call loop.create_task(while_loop()) followed by loop.create_task(some_func()) and then loop.run_forever().

  • loop.run_until_complete(asyncio.gather(while_loop(), some_func())) run the event loop until both the specified coroutines finish. This (wait for all the tasks) is apparently what you expected loop.run_until_complete() to do automatically even if you name only one, except it doesn’t work like that, it stops as soon as the specified coroutine finishes. asyncio.gather can be used to wait for multiple coroutines at once. For a more fine-tuned control of waiting, also see asyncio.wait.

Since one of your coroutines runs forever, the last two options will be equivalent and will result in the expected output.

Answered By: user4815162342