Event loops are used to execute and generally manage asynchronous tasks. Any asynchronous program in Python must directly or indirectly interact with the event loop.
Some higher level asyncio functions such as asyncio.run()
simplifies using event loops. For example when you execute an asynchronous task with asyncio.run()
, you do not have to manually interact with the event loop, as the creation, usage, and closing of the loop is handled automatically in the background
Consider the following example:
In the above example, we have defined three coroutines; display_nums()
, display_letters()
and main()
. We awaited display_nums()
and display_letters()
in main()
, then used asyncio.run()
function to execute main()
.
When you use asyncio.run()
function to execute a coroutine as in above, you do not have to directly interact with the event loop as this is handled automatically. In the background, the asyncio.run()
performs the following operations:
- Creates a new event loop.
- Sets the created event loop as the current event loop for the running thread.
- Executes the coroutine passed to it using the created event loop.
- Closes the event loop.
Note: Using the asyncio.run()
function is usually sufficient for most practical cases. However, in some cases you may need to manually interact with the event loop, this is what the following sections will cover.
Create or Get the event loop
There are several asyncio functions we can use to create or get the event loop.
Create a new event loop
To create a new loop, use the asyncio.new_event_loop()
function.
The loop.is_running()
and loop.is_closed()
methods can be used to get the state of the loop. The loop.close()
method closes the event loop, a closed event loop cannot be used to execute tasks anymore.
Get thread's current event loop
An event loop can be associated with thread. For example, whenever a Python program starts, an event loop is created and assigned to the main thread. We can use and reuse this event loop, instead of creating a new one.
To access the current loop for the running thread, we use the asyncio.get_event_loop()
function.
Note that calling the, get_event_loop()
method will return the same loop with each call.
In some cases, there may be no event loop that is associated with the running thread, in such a case, Python may issue a warning or even raise an exception. You can use the set_event_loop()
function to set the event loop for the current thread, so that this loop will be returned whenever the get_event_loop()
function is called in the thread.
Get the running event loop
Inside of a coroutine function, we can access the event loop in which the coroutine is being executed using the asyncio.get_running_loop()
function. Consider the following example:
In the above examples, we have seen how we can create or access an event loop, let us now look at how to make the event loop do what they are made for.
How to Run tasks/coroutines in an event loop
The basic tool for executing a coroutine inside of an event loop is the loop.run_until_complete()
method. As the name of the method suggests, the method runs a task/coroutine given as argument until it is fully executed.
In the above example, we have a single coroutine function, display_nums()
, we created a new event loop then used the loop.run_until_complete()
method to execute the coroutine.
run multiple coroutines
The primary goal of asynchronous programming is to execute multiple coroutines in a non-blocking manner. This means that if a delay is encountered in the running coroutine, the coroutine can be suspended to give way for another coroutine to be executed before resuming. At the start, we saw how we can achieve this with asyncio.run()
function by gathering the coroutines inside of the another coroutine eg main
.
To manually execute multiple tasks/coroutines using loop.run_until_complete()
, we will need to follow the steps outlined below:
- Create/get the event loop.
- Schedule each coroutine for execution using the l
oop.create_task()
method. - Combine the scheduled coroutines into a single runable object using
asyncio.gather()
function. - Call the
loop.run_until_complete()
method with the object obtained above in step 4.