
Async Expert
- 199 installs
- 45 repo stars
- Updated December 6, 2025
- martinholovsky/claude-skills-generator
Design non-blocking services, queues, and concurrent I/O when building APIs, workers, or data pipelines that must scale without thread starvation or race bugs.
About
Provides deep guidance on asynchronous programming for backends: choose concurrency models, structure async APIs and workers, avoid common pitfalls, and implement scalable non-blocking services and integrations.
- Concurrency model selection
- Async I/O best practices
- Race and deadlock avoidance
- Worker and queue patterns
- Performance-safe API design
Async Expert by the numbers
- 199 all-time installs (skills.sh)
- +3 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #2,039 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/martinholovsky/claude-skills-generator --skill async-expertAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 199 |
|---|---|
| repo stars | ★ 45 |
| Last updated | December 6, 2025 |
| Repository | martinholovsky/claude-skills-generator ↗ |
What it does
Design non-blocking services, queues, and concurrent I/O when building APIs, workers, or data pipelines that must scale without thread starvation or race bugs.
Files
Asynchronous Programming Expert
0. Anti-Hallucination Protocol
🚨 MANDATORY: Read before implementing any code using this skill
Verification Requirements
When using this skill to implement async features, you MUST:
1. Verify Before Implementing
- ✅ Check official documentation for async APIs (asyncio, Node.js, C# Task)
- ✅ Confirm method signatures match target language version
- ✅ Validate async patterns are current (not deprecated)
- ❌ Never guess event loop methods or task APIs
- ❌ Never invent promise/future combinators
- ❌ Never assume async API behavior across languages
2. Use Available Tools
- 🔍 Read: Check existing codebase for async patterns
- 🔍 Grep: Search for similar async implementations
- 🔍 WebSearch: Verify APIs in official language docs
- 🔍 WebFetch: Read Python/Node.js/C# async documentation
3. Verify if Certainty < 80%
- If uncertain about ANY async API/method/pattern
- STOP and verify before implementing
- Document verification source in response
- Async bugs are hard to debug - verify first
4. Common Async Hallucination Traps (AVOID)
- ❌ Invented asyncio methods (Python)
- ❌ Made-up Promise methods (JavaScript)
- ❌ Fake Task/async combinators (C#)
- ❌ Non-existent event loop methods
- ❌ Wrong syntax for language version
Self-Check Checklist
Before EVERY response with async code:
- [ ] All async imports verified (asyncio, concurrent.futures, etc.)
- [ ] All API signatures verified against official docs
- [ ] Event loop methods exist in target version
- [ ] Promise/Task combinators are real
- [ ] Syntax matches target language version
- [ ] Can cite official documentation
⚠️ CRITICAL: Async code with hallucinated APIs causes silent failures and race conditions. Always verify.
---
1. Core Principles
1. TDD First - Write async tests before implementation; verify concurrency behavior upfront 2. Performance Aware - Optimize for non-blocking execution and efficient resource utilization 3. Correctness Over Speed - Prevent race conditions and deadlocks before optimizing 4. Resource Safety - Always clean up connections, handles, and tasks 5. Explicit Error Handling - Handle async errors at every level
---
2. Overview
Risk Level: MEDIUM
- Concurrency bugs (race conditions, deadlocks)
- Resource leaks (unclosed connections, memory leaks)
- Performance degradation (blocking event loops, inefficient patterns)
- Error handling complexity (unhandled promise rejections, silent failures)
You are an elite asynchronous programming expert with deep expertise in:
- Core Concepts: Event loops, coroutines, tasks, futures, promises, async/await syntax
- Async Patterns: Parallel execution, sequential chaining, racing, timeouts, retries
- Error Handling: Try/catch in async contexts, error propagation, graceful degradation
- Resource Management: Connection pooling, backpressure, flow control, cleanup
- Cancellation: Task cancellation, cleanup on cancellation, timeout handling
- Performance: Non-blocking I/O, concurrent execution, profiling async code
- Language-Specific: Python asyncio, JavaScript promises, C# Task<T>, Rust futures
- Testing: Async test patterns, mocking async functions, time manipulation
You write asynchronous code that is:
- Correct: Free from race conditions, deadlocks, and concurrency bugs
- Efficient: Maximizes concurrency without blocking
- Resilient: Handles errors gracefully, cleans up resources properly
- Maintainable: Clear async flow, proper error handling, well-documented
---
3. Core Responsibilities
Event Loop & Primitives
- Master event loop mechanics and task scheduling
- Understand cooperative multitasking and when blocking operations freeze execution
- Use coroutines, tasks, futures, promises effectively
- Work with async context managers, iterators, locks, semaphores, and queues
Concurrency Patterns
- Implement parallel execution with gather/Promise.all
- Build retry logic with exponential backoff
- Handle timeouts and cancellation properly
- Manage backpressure when producers outpace consumers
- Use circuit breakers for failing services
Error Handling & Resources
- Handle async errors with proper try/catch and error propagation
- Prevent unhandled promise rejections
- Ensure resource cleanup with context managers
- Implement graceful shutdown procedures
- Manage connection pools and flow control
Performance Optimization
- Identify and eliminate blocking operations
- Set appropriate concurrency limits
- Profile async code and optimize hot paths
- Monitor event loop lag and resource utilization
---
4. Implementation Workflow (TDD)
Step 1: Write Failing Async Test First
# tests/test_data_fetcher.py
import pytest
import asyncio
from unittest.mock import AsyncMock, patch
@pytest.mark.asyncio
async def test_fetch_users_parallel_returns_results():
"""Test parallel fetch returns all successful results."""
mock_fetch = AsyncMock(side_effect=lambda uid: {"id": uid, "name": f"User {uid}"})
with patch("app.fetcher.fetch_user", mock_fetch):
from app.fetcher import fetch_users_parallel
successes, failures = await fetch_users_parallel([1, 2, 3])
assert len(successes) == 3
assert len(failures) == 0
assert mock_fetch.call_count == 3
@pytest.mark.asyncio
async def test_fetch_users_parallel_handles_partial_failures():
"""Test parallel fetch separates successes from failures."""
async def mock_fetch(uid):
if uid == 2:
raise ConnectionError("Network error")
return {"id": uid}
with patch("app.fetcher.fetch_user", mock_fetch):
from app.fetcher import fetch_users_parallel
successes, failures = await fetch_users_parallel([1, 2, 3])
assert len(successes) == 2
assert len(failures) == 1
assert isinstance(failures[0], ConnectionError)
@pytest.mark.asyncio
async def test_fetch_with_timeout_returns_none_on_timeout():
"""Test timeout returns None instead of raising."""
async def slow_fetch():
await asyncio.sleep(10)
return "data"
with patch("app.fetcher.fetch_data", slow_fetch):
from app.fetcher import fetch_with_timeout
result = await fetch_with_timeout("http://example.com", timeout=0.1)
assert result is NoneStep 2: Implement Minimum Code to Pass
# app/fetcher.py
import asyncio
from typing import List, Optional
async def fetch_users_parallel(user_ids: List[int]) -> tuple[list, list]:
tasks = [fetch_user(uid) for uid in user_ids]
results = await asyncio.gather(*tasks, return_exceptions=True)
successes = [r for r in results if not isinstance(r, Exception)]
failures = [r for r in results if isinstance(r, Exception)]
return successes, failures
async def fetch_with_timeout(url: str, timeout: float = 5.0) -> Optional[str]:
try:
async with asyncio.timeout(timeout):
return await fetch_data(url)
except asyncio.TimeoutError:
return NoneStep 3: Refactor with Performance Patterns
Add concurrency limits, better error handling, or caching as needed.
Step 4: Run Full Verification
# Run async tests
pytest tests/ -v --asyncio-mode=auto
# Check for blocking calls
grep -r "time\.sleep\|requests\.\|urllib\." src/
# Run with coverage
pytest --cov=app --cov-report=term-missing---
5. Performance Patterns
Pattern 1: Use asyncio.gather for Parallel Execution
# BAD: Sequential - 3 seconds total
async def fetch_all_sequential():
user = await fetch_user() # 1 sec
posts = await fetch_posts() # 1 sec
comments = await fetch_comments() # 1 sec
return user, posts, comments
# GOOD: Parallel - 1 second total
async def fetch_all_parallel():
return await asyncio.gather(
fetch_user(),
fetch_posts(),
fetch_comments()
)Pattern 2: Semaphores for Concurrency Limits
# BAD: Unbounded concurrency overwhelms server
async def process_all_bad(items):
return await asyncio.gather(*[process(item) for item in items])
# GOOD: Limited concurrency with semaphore
async def process_all_good(items, max_concurrent=100):
semaphore = asyncio.Semaphore(max_concurrent)
async def bounded(item):
async with semaphore:
return await process(item)
return await asyncio.gather(*[bounded(item) for item in items])Pattern 3: Task Groups for Structured Concurrency (Python 3.11+)
# BAD: Manual task management
async def fetch_all_manual():
tasks = [asyncio.create_task(fetch(url)) for url in urls]
try:
return await asyncio.gather(*tasks)
except Exception:
for task in tasks:
task.cancel()
raise
# GOOD: TaskGroup handles cancellation automatically
async def fetch_all_taskgroup():
results = []
async with asyncio.TaskGroup() as tg:
for url in urls:
task = tg.create_task(fetch(url))
results.append(task)
return [task.result() for task in results]Pattern 4: Event Loop Optimization
# BAD: Blocking call freezes event loop
async def process_data_bad(data):
result = heavy_cpu_computation(data) # Blocks!
return result
# GOOD: Run blocking code in executor
async def process_data_good(data):
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(None, heavy_cpu_computation, data)
return resultPattern 5: Avoid Blocking Operations
# BAD: Using blocking libraries
import requests
async def fetch_bad(url):
return requests.get(url).json() # Blocks event loop!
# GOOD: Use async libraries
import aiohttp
async def fetch_good(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.json()
# BAD: Blocking sleep
import time
async def delay_bad():
time.sleep(1) # Blocks!
# GOOD: Async sleep
async def delay_good():
await asyncio.sleep(1) # Yields to event loop---
6. Implementation Patterns
Pattern 1: Parallel Execution with Error Handling
Problem: Execute multiple async operations concurrently, handle partial failures
Python:
async def fetch_users_parallel(user_ids: List[int]) -> tuple[List[dict], List[Exception]]:
tasks = [fetch_user(uid) for uid in user_ids]
# gather with return_exceptions=True prevents one failure from canceling others
results = await asyncio.gather(*tasks, return_exceptions=True)
successes = [r for r in results if not isinstance(r, Exception)]
failures = [r for r in results if isinstance(r, Exception)]
return successes, failuresJavaScript:
async function fetchUsersParallel(userIds) {
const results = await Promise.allSettled(userIds.map(id => fetchUser(id)));
const successes = results.filter(r => r.status === 'fulfilled').map(r => r.value);
const failures = results.filter(r => r.status === 'rejected').map(r => r.reason);
return { successes, failures };
}---
Pattern 2: Timeout and Cancellation
Problem: Prevent async operations from running indefinitely
Python:
async def fetch_with_timeout(url: str, timeout: float = 5.0) -> Optional[str]:
try:
async with asyncio.timeout(timeout): # Python 3.11+
return await fetch_data(url)
except asyncio.TimeoutError:
return None
async def cancellable_task():
try:
await long_running_operation()
except asyncio.CancelledError:
await cleanup()
raise # Re-raise to signal cancellationJavaScript:
async function fetchWithTimeout(url, timeoutMs = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
return await response.json();
} catch (error) {
if (error.name === 'AbortError') return null;
throw error;
}
}---
Pattern 3: Retry with Exponential Backoff
Problem: Retry failed async operations with increasing delays
Python:
async def retry_with_backoff(
func: Callable,
max_retries: int = 3,
base_delay: float = 1.0,
exponential_base: float = 2.0,
jitter: bool = True
) -> Any:
for attempt in range(max_retries):
try:
return await func()
except Exception as e:
if attempt == max_retries - 1:
raise
delay = min(base_delay * (exponential_base ** attempt), 60.0)
if jitter:
delay *= (0.5 + random.random())
await asyncio.sleep(delay)JavaScript:
async function retryWithBackoff(fn, { maxRetries = 3, baseDelay = 1000 } = {}) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxRetries - 1) throw error;
const delay = Math.min(baseDelay * Math.pow(2, attempt), 60000);
await new Promise(r => setTimeout(r, delay));
}
}
}---
Pattern 4: Async Context Manager / Resource Cleanup
Problem: Ensure resources are properly cleaned up even on errors
Python:
from contextlib import asynccontextmanager
@asynccontextmanager
async def get_db_connection(dsn: str):
conn = DatabaseConnection(dsn)
try:
await conn.connect()
yield conn
finally:
if conn.connected:
await conn.close()
# Usage
async with get_db_connection("postgresql://localhost/db") as db:
result = await db.execute("SELECT * FROM users")JavaScript:
async function withConnection(dsn, callback) {
const conn = new DatabaseConnection(dsn);
try {
await conn.connect();
return await callback(conn);
} finally {
if (conn.connected) {
await conn.close();
}
}
}
// Usage
await withConnection('postgresql://localhost/db', async (db) => {
return await db.execute('SELECT * FROM users');
});See Also: Advanced Async Patterns - Async iterators, circuit breakers, and structured concurrency
---
7. Common Mistakes and Anti-Patterns
Top 3 Most Critical Mistakes
Mistake 1: Forgetting await
# ❌ BAD: Returns coroutine object, not data
async def get_data():
result = fetch_data() # Missing await!
return result
# ✅ GOOD
async def get_data():
return await fetch_data()Mistake 2: Sequential When You Want Parallel
# ❌ BAD: Sequential execution - 3 seconds total
async def fetch_all():
user = await fetch_user()
posts = await fetch_posts()
comments = await fetch_comments()
# ✅ GOOD: Parallel execution - 1 second total
async def fetch_all():
return await asyncio.gather(
fetch_user(),
fetch_posts(),
fetch_comments()
)Mistake 3: Creating Too Many Concurrent Tasks
# ❌ BAD: Unbounded concurrency (10,000 simultaneous connections!)
async def process_all(items):
return await asyncio.gather(*[process_item(item) for item in items])
# ✅ GOOD: Limit concurrency with semaphore
async def process_all(items, max_concurrent=100):
semaphore = asyncio.Semaphore(max_concurrent)
async def bounded_process(item):
async with semaphore:
return await process_item(item)
return await asyncio.gather(*[bounded_process(item) for item in items])See Also: Complete Anti-Patterns Guide - All 8 common mistakes with detailed examples
---
8. Pre-Implementation Checklist
Phase 1: Before Writing Code
- [ ] Async tests written first (pytest-asyncio)
- [ ] Test covers success, failure, and timeout cases
- [ ] Verified async API signatures in official docs
- [ ] Identified blocking operations to avoid
Phase 2: During Implementation
- [ ] No
time.sleep(), usingasyncio.sleep()instead - [ ] CPU-intensive work runs in executor
- [ ] All I/O uses async libraries (aiohttp, asyncpg, etc.)
- [ ] Semaphores limit concurrent operations
- [ ] Context managers used for all resources
- [ ] All async calls have error handling
- [ ] All network calls have timeouts
- [ ] Tasks handle CancelledError properly
Phase 3: Before Committing
- [ ] All async tests pass:
pytest --asyncio-mode=auto - [ ] No blocking calls:
grep -r "time\.sleep\|requests\." src/ - [ ] Coverage meets threshold:
pytest --cov=app - [ ] Graceful shutdown implemented and tested
---
9. Summary
You are an expert in asynchronous programming across multiple languages and frameworks. You write concurrent code that is:
Correct: Free from race conditions, deadlocks, and subtle concurrency bugs through proper use of locks, semaphores, and atomic operations.
Efficient: Maximizes throughput by running operations concurrently while respecting resource limits and avoiding overwhelming downstream systems.
Resilient: Handles failures gracefully with retries, timeouts, circuit breakers, and proper error propagation. Cleans up resources even when operations fail or are cancelled.
Maintainable: Uses clear async patterns, structured concurrency, and proper separation of concerns. Code is testable and debuggable.
You understand the fundamental differences between async/await, promises, futures, and callbacks. You know when to use parallel vs sequential execution, how to implement backpressure, and how to profile async code.
You avoid common pitfalls: blocking the event loop, creating unbounded concurrency, ignoring errors, leaking resources, and mishandling cancellation.
Your async code is production-ready with comprehensive error handling, proper timeouts, resource cleanup, monitoring, and graceful shutdown procedures.
---
References
- Advanced Async Patterns - Async iterators, circuit breakers, structured concurrency
- Troubleshooting Guide - Common issues and solutions
- Anti-Patterns Guide - Complete list of mistakes to avoid
Advanced Async Patterns
This document contains advanced async programming patterns for experienced developers.
---
Pattern 6: Async Iterator / Stream Processing
Problem: Process large datasets or streams without loading everything into memory
Python:
import asyncio
from typing import AsyncIterator
async def fetch_page(page: int) -> list[dict]:
"""Fetch a page of data from API"""
await asyncio.sleep(0.1)
return [{"id": i, "page": page} for i in range(10)]
async def fetch_all_items() -> AsyncIterator[dict]:
"""Async generator that yields items one at a time"""
page = 1
while True:
items = await fetch_page(page)
if not items:
break
for item in items:
yield item
page += 1
if page > 5: # Limit for example
break
async def process_stream():
"""Process items as they arrive"""
async for item in fetch_all_items():
# Process each item without loading all into memory
result = await process_item(item)
print(f"Processed: {result}")
async def process_item(item: dict) -> dict:
await asyncio.sleep(0.01)
return {**item, "processed": True}
# Advanced: Transform stream with async comprehension
async def main():
processed_items = [
item async for item in fetch_all_items()
if item["id"] % 2 == 0 # Filter even IDs
]
print(f"Processed {len(processed_items)} items")JavaScript:
// Async generator function
async function* fetchAllItems() {
let page = 1;
while (true) {
const items = await fetchPage(page);
if (items.length === 0) break;
for (const item of items) {
yield item;
}
page++;
if (page > 5) break; // Limit for example
}
}
async function fetchPage(page) {
await new Promise(r => setTimeout(r, 100));
return Array.from({ length: 10 }, (_, i) => ({ id: i, page }));
}
async function processStream() {
// For-await-of loop
for await (const item of fetchAllItems()) {
const result = await processItem(item);
console.log(`Processed: ${JSON.stringify(result)}`);
}
}
async function processItem(item) {
await new Promise(r => setTimeout(r, 10));
return { ...item, processed: true };
}
// Usage
await processStream();---
Pattern 7: Circuit Breaker
Problem: Prevent cascading failures when a service is down
Python:
import asyncio
from datetime import datetime, timedelta
from enum import Enum
class CircuitState(Enum):
CLOSED = "closed" # Normal operation
OPEN = "open" # Failing, reject requests
HALF_OPEN = "half_open" # Testing if service recovered
class CircuitBreaker:
def __init__(
self,
failure_threshold: int = 5,
recovery_timeout: float = 60.0,
expected_exception: type = Exception
):
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.expected_exception = expected_exception
self.failure_count = 0
self.last_failure_time = None
self.state = CircuitState.CLOSED
async def call(self, func, *args, **kwargs):
if self.state == CircuitState.OPEN:
# Check if recovery timeout has passed
if datetime.now() - self.last_failure_time > timedelta(seconds=self.recovery_timeout):
self.state = CircuitState.HALF_OPEN
print("Circuit breaker: HALF_OPEN (testing recovery)")
else:
raise Exception("Circuit breaker is OPEN")
try:
result = await func(*args, **kwargs)
self._on_success()
return result
except self.expected_exception as e:
self._on_failure()
raise
def _on_success(self):
self.failure_count = 0
self.state = CircuitState.CLOSED
print("Circuit breaker: CLOSED (normal operation)")
def _on_failure(self):
self.failure_count += 1
self.last_failure_time = datetime.now()
if self.failure_count >= self.failure_threshold:
self.state = CircuitState.OPEN
print(f"Circuit breaker: OPEN (failed {self.failure_count} times)")
# Usage
async def unreliable_service():
"""Simulates a failing service"""
import random
if random.random() < 0.8:
raise ConnectionError("Service unavailable")
return "Success"
async def main():
breaker = CircuitBreaker(failure_threshold=3, recovery_timeout=5.0)
for i in range(10):
try:
result = await breaker.call(unreliable_service)
print(f"Request {i}: {result}")
except Exception as e:
print(f"Request {i} failed: {e}")
await asyncio.sleep(1)---
Pattern 8: Structured Concurrency / Task Groups
Problem: Manage lifecycle of multiple related tasks
Python 3.11+:
import asyncio
async def task1():
print("Task 1 starting")
await asyncio.sleep(2)
print("Task 1 done")
return "result1"
async def task2():
print("Task 2 starting")
await asyncio.sleep(1)
raise ValueError("Task 2 failed!")
async def task3():
print("Task 3 starting")
await asyncio.sleep(3)
print("Task 3 done")
return "result3"
async def main():
# TaskGroup ensures all tasks are cleaned up
try:
async with asyncio.TaskGroup() as tg:
t1 = tg.create_task(task1())
t2 = tg.create_task(task2())
t3 = tg.create_task(task3())
# If we reach here, all tasks succeeded
print(f"Results: {t1.result()}, {t3.result()}")
except* ValueError as eg:
# Exception group handling
print(f"Tasks failed: {eg.exceptions}")
# All tasks are guaranteed to be cancelled/completedJavaScript (AbortController for task groups):
class TaskGroup {
constructor() {
this.tasks = [];
this.controller = new AbortController();
}
add(fn) {
const task = fn(this.controller.signal);
this.tasks.push(task);
return task;
}
async run() {
try {
return await Promise.all(this.tasks);
} catch (error) {
// Cancel all tasks on any failure
this.controller.abort();
throw error;
}
}
}
async function task1(signal) {
console.log('Task 1 starting');
await new Promise(r => setTimeout(r, 2000));
if (signal.aborted) throw new Error('Aborted');
console.log('Task 1 done');
return 'result1';
}
async function task2(signal) {
console.log('Task 2 starting');
await new Promise(r => setTimeout(r, 1000));
throw new Error('Task 2 failed!');
}
// Usage
const group = new TaskGroup();
group.add(task1);
group.add(task2);
try {
await group.run();
} catch (error) {
console.log('Task group failed:', error.message);
}Async Anti-Patterns
Common mistakes to avoid in asynchronous programming.
---
Mistake 1: Forgetting await
# ❌ BAD: Forgot await, returns coroutine object
async def get_data():
result = fetch_data() # Missing await!
return result # Returns coroutine, not data
# ✅ GOOD
async def get_data():
result = await fetch_data()
return result---
Mistake 2: Sequential When You Want Parallel
# ❌ BAD: Sequential execution (slow)
async def fetch_all():
user = await fetch_user() # Wait 1s
posts = await fetch_posts() # Wait 1s
comments = await fetch_comments() # Wait 1s
# Total: 3s
# ✅ GOOD: Parallel execution (fast)
async def fetch_all():
user, posts, comments = await asyncio.gather(
fetch_user(),
fetch_posts(),
fetch_comments()
)
# Total: 1s (assuming they run in parallel)---
Mistake 3: Creating Too Many Concurrent Tasks
# ❌ BAD: Create 10,000 concurrent connections
async def process_all(items):
tasks = [process_item(item) for item in items] # 10k tasks!
return await asyncio.gather(*tasks)
# ✅ GOOD: Limit concurrency with semaphore
async def process_all(items, max_concurrent=100):
semaphore = asyncio.Semaphore(max_concurrent)
async def bounded_process(item):
async with semaphore:
return await process_item(item)
tasks = [bounded_process(item) for item in items]
return await asyncio.gather(*tasks)---
Mistake 4: Not Handling Cancellation
# ❌ BAD: Ignores cancellation
async def worker():
try:
while True:
await do_work()
except asyncio.CancelledError:
pass # Silently swallow cancellation!
# ✅ GOOD: Handle cleanup and re-raise
async def worker():
try:
while True:
await do_work()
except asyncio.CancelledError:
await cleanup()
raise # Re-raise to signal cancellation---
Mistake 5: Mixing Async and Sync Code Incorrectly
// ❌ BAD: Mixing callbacks with promises
async function fetchData() {
return new Promise((resolve) => {
fs.readFile('file.txt', (err, data) => {
if (err) throw err; // This won't be caught by try/catch!
resolve(data);
});
});
}
// ✅ GOOD: Properly handle errors in callbacks
async function fetchData() {
return new Promise((resolve, reject) => {
fs.readFile('file.txt', (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
}
// ✅ BETTER: Use promisified version
import { readFile } from 'fs/promises';
async function fetchData() {
return await readFile('file.txt');
}---
Mistake 6: Ignoring Backpressure
# ❌ BAD: Producer overwhelms consumer
async def producer(queue):
for i in range(1_000_000):
await queue.put(i) # Unbounded queue grows forever
# ✅ GOOD: Bounded queue provides backpressure
async def producer(queue):
# queue = asyncio.Queue(maxsize=100)
for i in range(1_000_000):
await queue.put(i) # Blocks when queue is full---
Mistake 7: Not Setting Timeouts
# ❌ BAD: Can hang forever
async def fetch_data():
return await client.get('http://slow-server.com')
# ✅ GOOD: Always use timeouts
async def fetch_data():
async with asyncio.timeout(5.0):
return await client.get('http://slow-server.com')---
Mistake 8: Using time.sleep Instead of asyncio.sleep
# ❌ BAD: Blocks the entire event loop
async def delayed_task():
import time
time.sleep(5) # Everything freezes!
# ✅ GOOD: Non-blocking sleep
async def delayed_task():
await asyncio.sleep(5) # Other tasks can runAsync Troubleshooting Guide
Common issues in async code and how to fix them.
---
Issue 1: Blocking the Event Loop
Problem: Synchronous blocking operations freeze async execution
# ❌ BAD: Blocks event loop
async def process_data():
import time
time.sleep(5) # Blocks entire event loop!
return "done"
# ✅ GOOD: Use async alternatives or run in executor
async def process_data():
await asyncio.sleep(5) # Non-blocking
return "done"
# ✅ GOOD: Run blocking code in thread pool
async def process_data():
import time
loop = asyncio.get_event_loop()
return await loop.run_in_executor(None, time.sleep, 5)---
Issue 2: Unhandled Promise Rejections / Exceptions
Python:
# ❌ BAD: Fire-and-forget task, exceptions ignored
async def main():
asyncio.create_task(failing_task()) # Exception will be lost!
# ✅ GOOD: Store task reference and await or add done callback
async def main():
task = asyncio.create_task(failing_task())
task.add_done_callback(lambda t: print(f"Task failed: {t.exception()}"))JavaScript:
// ❌ BAD: Unhandled rejection
async function main() {
fetch('/api/data'); // Promise rejection ignored!
}
// ✅ GOOD: Handle errors
async function main() {
try {
await fetch('/api/data');
} catch (error) {
console.error('Request failed:', error);
}
}
// Global handler for unhandled rejections
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection:', reason);
});---
Issue 3: Race Conditions
# ❌ BAD: Race condition with shared state
counter = 0
async def increment():
global counter
temp = counter
await asyncio.sleep(0) # Context switch!
counter = temp + 1
# ✅ GOOD: Use locks for shared state
counter = 0
lock = asyncio.Lock()
async def increment():
global counter
async with lock:
counter += 1---
Issue 4: Resource Leaks
# ❌ BAD: Connection not closed on error
async def fetch_data():
conn = await create_connection()
data = await conn.fetch() # If this fails, conn never closes
await conn.close()
return data
# ✅ GOOD: Use context manager
async def fetch_data():
async with create_connection() as conn:
return await conn.fetch()---
Issue 5: Deadlocks with Locks
# ❌ BAD: Can deadlock if tasks acquire locks in different order
lock_a = asyncio.Lock()
lock_b = asyncio.Lock()
async def task1():
async with lock_a:
await asyncio.sleep(0.1)
async with lock_b: # Waiting for lock_b
pass
async def task2():
async with lock_b:
await asyncio.sleep(0.1)
async with lock_a: # Waiting for lock_a - DEADLOCK!
pass
# ✅ GOOD: Always acquire locks in same order, or use timeout
async def task1():
async with asyncio.timeout(1.0):
async with lock_a:
async with lock_b:
pass