Async Python lets you do more with less waiting. Here's how it works.
The Problem
import time
def fetch_data(url):
time.sleep(1) # Simulate network request
return f"Data from {url}"
# Sequential - takes 3 seconds
results = [fetch_data(url) for url in urls]While waiting for one request, you could be making others.
The Solution
import asyncio
async def fetch_data(url):
await asyncio.sleep(1) # Non-blocking wait
return f"Data from {url}"
async def main():
# Concurrent - takes ~1 second total
results = await asyncio.gather(
fetch_data("url1"),
fetch_data("url2"),
fetch_data("url3"),
)
asyncio.run(main())async and await
# async defines a coroutine
async def my_function():
# await pauses until result is ready
result = await some_async_operation()
return resultawait can only be used inside async functions.
Running Async Code
# Main entry point
asyncio.run(main())
# Or get the event loop
loop = asyncio.get_event_loop()
loop.run_until_complete(main())Concurrent Execution
gather - Run Multiple Tasks
async def main():
results = await asyncio.gather(
fetch("url1"),
fetch("url2"),
fetch("url3"),
)
# results = [result1, result2, result3]create_task - Fire and Manage
async def main():
task1 = asyncio.create_task(fetch("url1"))
task2 = asyncio.create_task(fetch("url2"))
# Do other work while tasks run
result1 = await task1
result2 = await task2as_completed - Process as Ready
async def main():
tasks = [fetch(url) for url in urls]
for coro in asyncio.as_completed(tasks):
result = await coro
print(f"Got: {result}") # Process immediatelyTimeouts
async def main():
try:
result = await asyncio.wait_for(
slow_operation(),
timeout=5.0
)
except asyncio.TimeoutError:
print("Operation timed out")Real HTTP Requests
Use httpx or aiohttp:
import httpx
async def fetch_all(urls):
async with httpx.AsyncClient() as client:
tasks = [client.get(url) for url in urls]
responses = await asyncio.gather(*tasks)
return [r.json() for r in responses]Async Context Managers
class AsyncResource:
async def __aenter__(self):
await self.connect()
return self
async def __aexit__(self, *args):
await self.disconnect()
async def main():
async with AsyncResource() as resource:
await resource.do_something()Async Iterators
async def fetch_pages():
page = 1
while True:
data = await fetch_page(page)
if not data:
break
yield data
page += 1
async def main():
async for page in fetch_pages():
process(page)Common Patterns
Semaphore - Limit Concurrency
async def fetch_with_limit(urls, max_concurrent=10):
semaphore = asyncio.Semaphore(max_concurrent)
async def fetch_one(url):
async with semaphore:
return await fetch(url)
return await asyncio.gather(*[fetch_one(url) for url in urls])Queue - Producer/Consumer
async def producer(queue):
for item in items:
await queue.put(item)
async def consumer(queue):
while True:
item = await queue.get()
await process(item)
queue.task_done()
async def main():
queue = asyncio.Queue()
producers = [asyncio.create_task(producer(queue))]
consumers = [asyncio.create_task(consumer(queue)) for _ in range(3)]
await asyncio.gather(*producers)
await queue.join() # Wait for all items processed
for c in consumers:
c.cancel()When to Use Async
Good for:
- I/O bound operations (network, disk)
- Many concurrent connections
- Web servers handling many requests
- Web scraping
Not helpful for:
- CPU-bound work (use multiprocessing)
- Simple scripts
- When you have few concurrent operations
Common Mistakes
Blocking in Async Code
# Bad - blocks the event loop
async def bad():
time.sleep(1) # Blocks everything!
# Good
async def good():
await asyncio.sleep(1) # Non-blockingForgetting await
# Wrong - returns coroutine object, not result
result = fetch_data()
# Right
result = await fetch_data()Mixing Sync and Async
# Can't await in regular function
def regular_function():
await async_thing() # SyntaxError!
# Must be async
async def async_function():
await async_thing() # OKRunning Sync Code in Async
import asyncio
def blocking_operation():
time.sleep(1)
return "done"
async def main():
# Run in thread pool
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(None, blocking_operation)My Guidelines
- Use async for I/O - network, database, files
- Limit concurrency - don't overwhelm servers
- Handle errors - use try/except in tasks
- Use httpx/aiohttp - not requests
- Keep it simple - don't async everything
Async adds complexity. Use it when you need concurrency.
React to this post: