How to Wait for the Async Function to Complete in Python
-
Use the
await
Keyword & Make an Asynchronous Function -
Use
create_task()
to Create Task to Fix an Issue
This article demonstrates how to create an asynchronous function and use the await
keyword to interrupt a process. We’ll also ’learn how to use tasks instead of threads in Python.
Use the await
Keyword & Make an Asynchronous Function
Asynchronous programming is not multi-threading; it is not multi-processing but concurrent programming.
We will not talk about the whole idea of concurrent programming and the entire coding pattern, but we will talk about the basic principles and how we can implement those in Python.
Now let’s look at a quick example; we have a Func_1
, Func_2
and Func_3
, which are being called.
Func_1()
Func_2()
Func_3()
If these functions are being called asynchronously, this means that we are going to call Func_1()
and then we are going to call Func_2()
.
When Func_1()
returns so, we are only going to call Func_2(),
and when Func_2()
returns then we are going to call Func_3()
.
If we use multi-threading or multi-processing, that would not be the same as asynchronous programming. Because in multi-threading, we would define three threads in this case, and we would run all of these functions at the same time.
Or, roughly simultaneously, we will try to run them simultaneously or at least create the illusion of simultaneous execution.
But, we want to do, let’s say, Func_1()
does something productive, and then it requests some data from a database, from an API, or it just sleeps in general just for the sake of waiting.
If that happens, we do not want to waste CPU time and start executing Func_2()
even though this function has not yet returned. So we can only run one task simultaneously; we are not doing any multi-processing or multi-threading.
But, if the Func_1()
is sleeping or waiting or being unproductive so we can utilize that time to start executing Func_2()
and maybe Func_3()
. To do asynchronous programming in Python, we must import a library called asyncio
.
Since we will not define the whole program as asynchronous, specific functions will be asynchronous; we need to use the async
keyword to specify an asynchronous function.
If we only have this Main_Func()
, the whole program will be asynchronous, but we will add other functions in the next example. Inside this function, we will use two print()
functions.
And, in between, we are going to sleep, but we are not going to sleep with time.sleep()
; we are going to use asyncio.sleep()
.
We need to use the await
keyword before calling asyncio.sleep()
, which means that we will wait for the second print statement to finish. Before finishing, we are not going to do anything else.
To run the Main_Func()
function we need to use asyncio.run()
and inside run()
function we will pass Main_Func()
function. We must call the Main_Func()
function; we are not just referring to it as multi-threading.
import asyncio
async def Main_Func():
print("Before waiting")
await asyncio.sleep(1)
print("After waiting")
asyncio.run(Main_Func())
Output:
Let’s introduce another asynchronous function called Func_2()
; we will print two statements and sleep for two seconds.
Inside the Main_Func()
function, instead of sleeping, we call the Func_2()
function with the await
keyword, but that will not be asynchronous.
import asyncio
async def Main_Func():
print("Before waiting")
await Func_2()
print("After waiting")
async def Func_2():
print("Func_2: Before waiting")
await asyncio.sleep(2)
print("Func_2: After waiting")
asyncio.run(Main_Func())
Since we are awaiting the function, which is the same as just calling it asynchronously, it will not execute this until all the instructions are done as we defined.
Before waiting
Func_2: Before waiting
Func_2: After waiting
After waiting
It is not asynchronous; however, if we want to do something like that when the Main_Func()
function calls, then the control should print the first print
statement of the Main_Func()
function.
And then, call the Func_2()
function, thus printing this function’s first print
statement.
While this function is sleeping, it should print the second print
statement of the Main_Func()
function, and once this is done, it should print the second print
statement of the Func_2()
function.
Use create_task()
to Create Task to Fix an Issue
To do that, we need to work with tasks
so, at the beginning of the Main_Func()
function, we are going to create a task while calling asyncio.create_task()
, and inside the task, we will pass Func_2()
.
It means that once we have some idle time, we will call that task.
import asyncio
async def Main_Func():
Task = asyncio.create_task(Func_2())
print("Before waiting")
print("After waiting")
async def Func_2():
print("Func_2: Before waiting")
await asyncio.sleep(2)
print("Func_2: After waiting")
asyncio.run(Main_Func())
After running the code, we can see the two print statements are printed from the Main_Func()
function then it is done. And the first print
statement is executed, but the execution is terminated before printing the second print
statement.
Before waiting
After waiting
Func_2: Before waiting
It is because the Main_Func()
is the main function, the control is not waiting for the Func_2()
function, which means that once the control reaches the end of the Main_Func()
function, the control stops executing.
The control does not need to wait for the second print
statement of the Func_2()
function to finish so that the control will skip it. To fix it, we will use await Task
at the end of the Main_Func()
function.
import asyncio
async def Main_Func():
Task = asyncio.create_task(Func_2())
print("Before waiting")
print("After waiting")
await Task
async def Func_2():
print("Func_2: Before waiting")
await asyncio.sleep(2)
print("Func_2: After waiting")
asyncio.run(Main_Func())
Now we can see that it is printed as it was defined.
Before waiting
After waiting
Func_2: Before waiting
Func_2: After waiting
If we want to see how this works asynchronously, we can do it using the sleep()
function in between print
statements.
It means we will execute a first print
statement of Main_Func()
, and then the control will sleep for a second, which means that the main function now has idle time.
And now, the task has time to be executed until the Main_Func()
is entirely run. Since the Main_Func()
function is sleeping means we now have CPU time available to start executing the Func_2()
function by calling it using the task.
But, the Func_2()
function also goes to sleep, which means that inside this function control waits for two seconds, and the control goes to the Main_Func()
function to print a second print
statement.
Then it terminates, meaning the control is no longer interested in the rest of the Func_2()
function.
async def Main_Func():
Task = asyncio.create_task(Func_2())
print("Before waiting")
await asyncio.sleep(1)
print("After waiting")
Output:
Before waiting
Func_2: Before waiting
After waiting
If we want to be interested in that, we have to use await Task
inside at the end of the Main_Func()
function.
It is asynchronous; as you can see, the order is that the Main_Func()
function prints the first print
statement, then Func_2()
prints first, then Main_Func()
prints the second as well as Func_2()
prints the second.
Because when the function goes to sleep, idle time is used so that no CPU time is wasted.
Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.
LinkedIn