Search code examples
pythonpython-asyncio

running asyncio task without await causes memory issue


I have a slow function call early in the run, which returns a value I do not use until much later. I do not want to await this function when I first call it.

# run start
asyncio.get_event_loop().create_task(slow_function_wrapper())

async def slow_function_wrapper(self):
  self.slow_value = await slow_function()

...
# later in the code
async def get_slow_value(self):
  if iscoroutine(self.slow_value):   #if the call still didn't finish, await it
    await self.slow_value
  return self.slow_value

my issue is, this seems to cause a memory leak. there's some reference still saved to the task, so even when it completes it does not clear it from memory.

Is there a standard way of not-awaiting async function? or clearing the reference once it's done?

This is python 3.7


Solution

  • Just use asyncio.create_task with add_done_callback

    import asyncio
    from functools import partial
    
    
    async def slow_function() -> int:
        await asyncio.sleep(5)
        return 100
    
    
    class SomeClass:
        def __init__(self):
            self.slow_value = None
    
        def slow_function_wrapper(self) -> None:
            task = asyncio.create_task(slow_function())  # we do not wait anything here
            task.add_done_callback(partial(callback, obj=self))
    
    
    def callback(fut: asyncio.Task, obj: SomeClass) -> None:
        if fut.exception():
            print("Error")
            obj.slow_value = -1
        else:
            print("OK")
            obj.slow_value = fut.result()
    
    
    async def result_pinger(obj: SomeClass) -> None:
        # we need to make asyncio loop busy with something
        while True:
            await asyncio.sleep(1)
            print(f"Result: {obj.slow_value}")
            if obj.slow_value is not None:
                break
    
    
    async def main():
        x = SomeClass()
        x.slow_function_wrapper()
        await result_pinger(obj=x)  # prevent asyncio loop from stopping
    
    
    if __name__ == '__main__':
        asyncio.run(main())