Search code examples
pythonpython-3.xasynchronouspython-asyncio

Asyncio.sleep causes script to End Immediately


In my simple asyncio Python program below, bar_loop is supposed to run continuously with a 1 second delay between loops.

Things run as expected when we have simply

    async def bar_loop(self):
        while True:
            print('bar')

However, when we add a asyncio.sleep(1), the loop will end instead of looping.

    async def bar_loop(self):
        while True:
            print('bar')
            await asyncio.sleep(1)

Why does asyncio.sleep() cause bar_loop to exit immediately? How can we let it loop with a 1 sec delay?

Full Example:

import asyncio
from typing import Optional

class Foo:
    def __init__(self):
        self.bar_loop_task: Optional[asyncio.Task] = None
        
    async def start(self):
        self.bar_loop_task = asyncio.create_task(self.bar_loop())

    async def stop(self):
        if self.bar_loop_task is not None:
            self.bar_loop_task.cancel()

    async def bar_loop(self):
        while True:
            print('bar')
            await asyncio.sleep(1)

if __name__ == '__main__':
    try:
        foo = Foo()
        asyncio.run(foo.start())
    except KeyboardInterrupt:
        asyncio.run(foo.stop())

Using Python 3.9.5 on Ubuntu 20.04.


Solution

  • This behavior has nothing to do with calling asyncio.sleep, but with the expected behavior of creating a task and doing nothing else. Tasks will run in parallel in the the asyncio loop, while other code that uses just coroutine and await expressions can be thought as if run in a linear pattern - however, as the are "out of the way" of the - let's call it "visible path of execution", they also won't prevent that flow.

    In this case, your program simply reaches the end of the start method, with nothing left being "awaited", the asyncio loop simply finishes its execution.

    If you have no explicit code to run in parallel to bar_loop, just await for the task. Change your start method to read:

    async def start(self):
        self.bar_loop_task = asyncio.create_task(self.bar_loop())
        try:
            await self.bar_loop_task
        except XXX:
            # handle excptions that might have taken place inside the task