pythonasynchronoussanic

rewriting sync as async: not wait on a func


As I read more, I feel more stupid about aysnc in python. So I decided to ask for a direct answer. How can I change the following code (using async or similar approaches) to achieve the desired result? Additionally, how can I do it in flask or sanic?

import time

def long_job():
    print('long job started')
    time.sleep(5)
    print('long job ended')

def main_job():
    long_job()
    time.sleep(1)
    print('main job returned')

main_job()

# expected result:
# 'long job started'
# 'main job returned'
# 'long job ended'

Basically, I do NOT want to await for long_job to end before returning my main_job. Thank you in advance. :)


Solution

  • Await asyncio's sleep() to yield time to other jobs (if you don't need to await something else). Use create_task() instead of await to start a job without blocking. Finally, you have to start the main job using the event loop.

    # Written in Python 3.7
    import asyncio
    
    async def long_job():
        print('long job started')
        await asyncio.sleep(5)
        print('long job ended')
    
    async def main_job():
        asyncio.create_task(long_job())
        await asyncio.sleep(1)
        print('main job returned')
    

    Your framework should start the event loop; you don't have to start it yourself. You can await or call create_task on main_job() from an async def function called by your framework, depending on if you want to block or not.


    If you want to test this without a framework, you'll have to start the loop yourself using asyncio.run(). This will stop immediately after its task completes, even if other tasks haven't finished yet. But this is easy enough to work around:

    async def loop_job():
        asyncio.create_task(main_job())
        while len(asyncio.Task.all_tasks()) > 1:  # Any task besides loop_job()?
            await asyncio.sleep(0.2)
    
    asyncio.run(loop_job())
    

    If you're implementing a framework yourself, you can use the more primitive loop.run_forever(), but you'd have to stop() it yourself.