Search code examples
pythontimeoutpython-asyncioasynccallback

asyncio: wait for async callback with timeout


I am not much used to asyncio, so perhaps this question is trivial.

I have a code running asynchronously, which will run a callback when done (the callback can be callable or awaitable). I would like to wait for the callback to be called, with timeout. I sense that it is conceptually a task, but I am not sure how to create the task but wait for it somewhere else.

import asyncio, inspect

async def expensivefunction(callback):
    # this is something which takes a lot of time
    await asyncio.sleep(10)
    # but eventually computes the result
    result=10
    # and calls the callback
    callback(result)
    if inspect.isawaitable(callback): await callback

# just print the result, for example
async def callback(result): print(result)

# main code async
async def myfunc():
    await expensivefunction(callback=callback)
    # this will wait for callback to be called within 5 seconds
    # if not, exception is thrown
    await asyncio.wait_for(...??,timeout=5)

asyncio.run(myfunc())

What would be the right approach to this?


Solution

  • Please find working example:

    import asyncio
    
    AWAIT_TIME = 5.0
    
    
    async def expensive_function():
        """this is something which takes a lot of time"""
        await asyncio.sleep(10)
        result = 10
    
        return result
    
    
    def callback(fut: asyncio.Future):
        """just prints result. Callback should be sync function"""
        if not fut.cancelled() and fut.done():
            print(fut.result())
        else:
            print("No results")
    
    
    async def amain():
        """Main async func in the app"""
        # create task
        task = asyncio.create_task(expensive_function())
        task.add_done_callback(callback)
        # try to await the task
        try:
            r = await asyncio.wait_for(task, timeout=AWAIT_TIME)
        except asyncio.TimeoutError as ex:
            print(ex)
        else:
            print(f"All work done fine: {r}")
        finally:
            print("App finished!")
    
    
    if __name__ == '__main__':
        asyncio.run(amain())
    
    

    If any questions, please let me know.