Asynchronous Programming
Perform multiple I/O operations concurrently without blocking.
Asynchronous programming allows you to perform multiple I/O-bound tasks concurrently without blocking execution. Python’s asyncio library provides the foundation for async code using the async and await keywords.
Why Asynchronous Programming?
Section titled “Why Asynchronous Programming?”Synchronous (Blocking) - Sequential Execution
Section titled “Synchronous (Blocking) - Sequential Execution”import time
def fetch_data(data_id: str, delay: float): print(f"Fetching {data_id}...") time.sleep(delay) # Blocks execution print(f"Fetched {data_id}") return data_id
# Sequential executionstart = time.time()fetch_data("Data 1", 2)fetch_data("Data 2", 2)print(f"Total time: {time.time() - start:.2f} seconds")# Total time: ~4 secondsAsynchronous (Non-Blocking) - Concurrent Execution
Section titled “Asynchronous (Non-Blocking) - Concurrent Execution”import asyncioimport time
async def fetch_data(data_id: str, delay: float): print(f"Fetching {data_id}...") await asyncio.sleep(delay) # Non-blocking print(f"Fetched {data_id}") return data_id
async def main(): # Run tasks concurrently start = time.time() results = await asyncio.gather( fetch_data("Data 1", 2), fetch_data("Data 2", 2) ) print(f"Total time: {time.time() - start:.2f} seconds") print("Results:", results)
asyncio.run(main())# Total time: ~2 seconds (both tasks run in parallel)Basic Async Concepts
Section titled “Basic Async Concepts”Defining Async Functions
Section titled “Defining Async Functions”import asyncio
async def greet(name: str): """Async function - returns a coroutine""" await asyncio.sleep(1) # Simulate I/O operation return f"Hello, {name}!"
# Call async functionasync def main(): result = await greet("Alice") print(result)
asyncio.run(main())Running Multiple Tasks
Section titled “Running Multiple Tasks”import asyncio
async def task(name: str, delay: float): print(f"Starting {name}...") await asyncio.sleep(delay) print(f"Finished {name}") return f"Result from {name}"
async def main(): # Run tasks concurrently results = await asyncio.gather( task("Task 1", 2), task("Task 2", 1), task("Task 3", 3) ) print("All tasks completed:", results)
asyncio.run(main())# Tasks run concurrently, not sequentiallyReal-World Example: Concurrent API Requests
Section titled “Real-World Example: Concurrent API Requests”import asyncioimport aiohttp
async def fetch_url(session: aiohttp.ClientSession, url: str): """Fetch a single URL""" async with session.get(url) as response: return await response.text()
async def fetch_multiple_urls(urls: list[str]): """Fetch multiple URLs concurrently""" async with aiohttp.ClientSession() as session: tasks = [fetch_url(session, url) for url in urls] results = await asyncio.gather(*tasks) return results
# Usageasync def main(): urls = [ "https://api.example.com/data1", "https://api.example.com/data2", "https://api.example.com/data3" ] results = await fetch_multiple_urls(urls) print(f"Fetched {len(results)} URLs")
asyncio.run(main())Async Context Managers
Section titled “Async Context Managers”You can create async context managers:
import asynciofrom contextlib import asynccontextmanager
@asynccontextmanagerasync def async_database_connection(connection_string: str): """Async context manager""" print("Connecting to database...") connection = await connect_to_database(connection_string) try: yield connection finally: print("Closing database connection...") await connection.close()
async def connect_to_database(connection_string: str): # Simulate async connection await asyncio.sleep(0.1) return {"status": "connected"}
# Usageasync def main(): async with async_database_connection("postgresql://localhost/db") as db: print("Using database:", db)
asyncio.run(main())Async Classes
Section titled “Async Classes”You can create classes with async methods:
import asyncio
class AsyncDataFetcher: """Class with async methods""" def __init__(self, base_url: str): self.base_url = base_url
async def fetch(self, endpoint: str): """Async method""" await asyncio.sleep(1) # Simulate network request return f"Data from {self.base_url}/{endpoint}"
async def fetch_multiple(self, endpoints: list[str]): """Fetch multiple endpoints concurrently""" tasks = [self.fetch(ep) for ep in endpoints] return await asyncio.gather(*tasks)
# Usageasync def main(): fetcher = AsyncDataFetcher("https://api.example.com") results = await fetcher.fetch_multiple(["users", "posts", "comments"]) print(results)
asyncio.run(main())When to Use Async
Section titled “When to Use Async”Use async for:
- I/O-bound operations (network requests, file I/O, database queries)
- Multiple concurrent operations
- Web servers and APIs
- Real-time applications