pythonpython-asyncioevent-loop

Even loop creation in Asyncio of Python


In asyncio library of Python, which method(s) create the event loop? Is it asyncio.run() / asyncio.get_event_loop() ? Also which method(s) start running the event loop? I tried searching; but it seems there are not very clear answers.


Solution

  • TL;DR

    For high-level API, it's creating automatically in asyncio.run() by asyncio.new_event_loop() or by a loop_factory function if provided. In low-level API, it's asyncio.new_event_loop(). asyncio.get_event_loop() returning a running event loop or, if didn't run, policy's event loop (asyncio.get_event_loop_policy().get_event_loop()). It doesn't create a new event loop.

    Structure of asyncio

    In asyncio, we have a high-level and low-level APIs. High-level APIs fit for situations when we need to run a simple application with 3 or 5 asynchronous functions. In turn, Low-level APIs needs to control a process of creating event loops, managing Future's instance properties (like name, running status etc.), setting of policies and so on.

    Creation of a new event loop

    How they're working?

    asyncio.run() makes four things:

    1. Creating a new event loop by functions above
    2. Setting a new event loop in current thread by asyncio.set_event_loop()
    3. Putting an asynchronous function to the event loop by asyncio.loop.create_task() where loop is asyncio.SelectorEventLoop or asyncio.ProactorEventLoop, depends from the system
    4. Starting an event loop by asyncio.run_until_complete()

    asyncio.new_event_loop() makes first clause, so you need to manually set a new event loop like in second clause and run

    Starting an event loop

    How mentioned above, asyncio.run() set a new event loop automatically. For low-level APIs, we can use:

    How it's clearly from names, first will be running to the last finished task, and second will be running forever

    Examples

    In all examples, code will be like this:

    import asyncio
    ...
    ...
    ...
    if __name__ == "__main__":
        main()
    

    where dots are rest of our code

    Here's equivalent examples on the high-level API:

    async def get_data():
        # Emulating a fetch from a remote server
        data = [
            {"name": "Jane", "age": 19},
            {"name": "John", "age": 24}
        ]
        await asyncio.sleep(1)
        return data
    
    
    def main():
        print(asyncio.run(get_data()))
    

    and low-level API:

    def init_loop():
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        return loop
    
    
    async def get_data():
        # Emulating a fetch from a remote server
        data = [
            {"name": "Jane", "age": 19},
            {"name": "John", "age": 24}
        ]
        await asyncio.sleep(1)
        return data
    
    
    def main():
        loop = init_loop()
        task = loop.create_task(get_data())
        loop.run_until_complete(task)
        print(task.result())
    

    Result:

    [{'name': 'Jane', 'age': 19}, {'name': 'John', 'age': 24}]