In the olden days of Python (before version 3.5), if you wanted to do something that took a long time to complete, like downloading a file or reading from a database, you had two options: block the main thread and wait for it to finish, or spawn multiple threads to handle different tasks simultaneously.
The first option was simple but not very efficient because it meant your program would freeze until the task finished running. The second option required more code and could lead to race conditions and other issues if you weren’t careful.
Enter asynchronous programming! With async/await syntax, we can write code that looks like synchronous code (i.e., no callbacks or promises) but runs in a non-blocking way. This means our program won’t freeze while waiting for long tasks to finish and can handle multiple tasks at once without spawning new threads.
Now asynchronous iteration, which is like regular iteration (for loop) but with async/await syntax. Here’s an example:
# Import the necessary libraries
import asyncio # Importing the asyncio library for asynchronous programming
from urllib.request import urlopen # Importing the urlopen function from the urllib.request library for making HTTP requests
# Define a function for downloading a webpage asynchronously
async def download_page(url): # Using the async keyword to define an asynchronous function
response = await asyncio.get_event_loop().run_in_executor(None, lambda: urlopen(url)) # Using the await keyword to wait for the response from the webpage to be returned asynchronously
return await response.read() # Using the await keyword to wait for the webpage content to be read asynchronously and return it
# Define a list of URLs to download
urls = ['https://www.google.com', 'https://www.facebook.com']
# Create an empty list to store the results
results = []
# Loop through the URLs and download the webpages asynchronously
for url in urls:
result = asyncio.get_event_loop().run_until_complete(download_page(url)) # Using the run_until_complete function to run the asynchronous function and wait for it to finish before moving on to the next iteration
results.append(result) # Appending the downloaded webpage content to the results list
# The results list now contains the downloaded webpage content for each URL in the urls list
In this example, we’re using the `asyncio` module to download two web pages asynchronously and store their contents in a list called `results`. The `get_event_loop()` function returns an event loop object that allows us to run coroutines (functions with async/await syntax) on it.
The `run_in_executor()` function is used to execute the `urlopen()` call in a separate thread, which makes it possible for our program to continue running while waiting for the response to come back from Google and Facebook. The `asyncio.get_event_loop().run_until_complete(…)` line runs the coroutine (i.e., function with async/await syntax) on the event loop object, which returns when all of its awaited operations have completed.
Now asynchronous context management, which is like regular context management (with `try` and `finally`) but with async/await syntax. Here’s an example:
# Import the necessary libraries
import asyncio
from random import randint
# Define a coroutine function that simulates rolling a dice and waiting for the result
async def roll_dice(num_rolls):
rolls = []
for _ in range(num_rolls):
await asyncio.sleep(randint(1, 3)) # simulate rolling a dice and waiting for the result
rolls.append(randint(1, 6))
return sum(rolls)
# Define a coroutine function that will be executed as the main function
async def main():
# Get the event loop object
loop = asyncio.get_event_loop()
# Use asynchronous context management to ensure the lock is released after the coroutine finishes
async with asyncio.Lock():
# Call the roll_dice coroutine and await its result
total = await roll_dice(5)
# Print the total of the dice rolls
print('Total:', total)
# Close the event loop
loop.close()
# Check if the script is being run directly
if __name__ == '__main__':
# Run the main coroutine function using the asyncio.run() function
asyncio.run(main())
# The script uses asynchronous programming to simulate rolling a dice and waiting for the result.
# It uses the asyncio library to create a coroutine function that can be executed on the event loop object.
# The roll_dice coroutine function uses the await keyword to pause the execution and wait for the result of the asyncio.sleep() function.
# The main coroutine function uses asynchronous context management to ensure the lock is released after the coroutine finishes.
# The asyncio.run() function is used to run the main coroutine function on the event loop.
# The script also uses the random library to generate random numbers for the dice rolls.
In this example, we’re using a lock to ensure that only one coroutine can access the `roll_dice()` function at a time (to prevent race conditions). The `with (asyncio.Lock(),):` line creates an asynchronous context manager that acquires and releases the lock automatically when entering and leaving the block of code inside it, respectively.
The `asyncio.run(main())` line runs the coroutine on the event loop object, which starts the program’s execution. The `__name__ == ‘__main__’:` check is used to ensure that this script can be run as a standalone program (i.e., when you call it from your terminal or IDE).
Asynchronous iteration and context management in Python, made simple with async/await syntax.