Understanding Python Async/Await: Key Insights and Lessons
Written on
Chapter 1: Introduction to Async Functions
In Python, the use of "async def" enables the creation of asynchronous functions. This keyword signifies that the function operates asynchronously, allowing other tasks to run concurrently while it is in progress.
Here's a traditional function for comparison:
def hello():
return 'hello'
When we print it, we see the function type:
print(hello) # Output: <function hello at ...>
Now, let's look at an asynchronous function:
async def hello():
return 'hello'
Printing this asynchronous function also returns its type:
print(hello) # Output: <function hello at ...>
Section 1.1: Understanding Coroutine Behavior
When we invoke an async function, it yields a coroutine rather than a direct return value:
def hello():
return 'hello'
print(hello()) # Output: hello
This is a regular function call that returns the string "hello". However, calling an async function like this:
async def hello():
return 'hello'
print(hello())
Triggers a RuntimeWarning indicating that the coroutine was never awaited:
RuntimeWarning: coroutine 'hello' was never awaited.
This highlights that coroutines typically require the await keyword for proper execution.
Subsection 1.1.1: What Constitutes a Coroutine?
A coroutine is a unique function that can pause and resume its execution. It can temporarily yield control to other coroutines, facilitating concurrent task management.
Section 1.2: Executing Coroutines with asyncio
To run a coroutine directly, we utilize asyncio.run():
import asyncio
async def hello():
print('running hello coroutine')
return 'hello'
asyncio.run(hello()) # Output: running hello coroutine
This method is the proper way to execute a coroutine. Notably, asyncio is included in the Python Standard Library, so no external installations are necessary.
Chapter 2: Utilizing Await for Coroutine Execution
We can also execute coroutines within another coroutine using the await keyword:
async def hello():
print('running hello coroutine')
return 'hello'
async def main():
x = await hello()
print(x)
asyncio.run(main())
This produces the output:
# running hello coroutine
# hello
The await hello() statement effectively pauses the main coroutine until hello() completes, allowing us to assign its return value to x.
Section 2.1: Await Restrictions
It’s important to note that the await keyword can only be used within functions defined with async def:
async def hello():
print('running hello coroutine')
return 'hello'
def test():
x = await hello() # This line will raise an error
test()
Attempting to use await outside of an asynchronous function results in a SyntaxError.
Section 2.2: Concurrent Execution with asyncio.gather
To execute multiple coroutines simultaneously, we can use asyncio.gather:
import asyncio
async def hello():
print('start')
await asyncio.sleep(1)
print('end')
async def main():
await asyncio.gather(hello(), hello(), hello())
asyncio.run(main())
The output will show:
# start
# start
# start
# end
# end
# end
Here’s what’s happening: asyncio.sleep(1) causes each coroutine to pause for one second, while asyncio.gather initiates three hello() coroutines at once. Hence, the "start" messages appear first, followed by the "end" messages after a brief pause.
If You Wish To Support Me As A Creator
Clap 50 times for this story! Leave a comment sharing your thoughts or highlighting your favorite part. Your small actions make a big difference, and I genuinely appreciate your support!
You can also connect with me on: