What is Async Programming?
Async programming handles operations that take time without blocking:
- Network requests
- File I/O
- Database queries
- External API calls
Instead of waiting, you continue and handle results when ready.
Sync vs Async
Synchronous
# Blocks until complete
result1 = fetch_data_1() # Wait...
result2 = fetch_data_2() # Wait...
result3 = fetch_data_3() # Wait...
# Total: sum of all wait times
Asynchronous
# Start all at once
task1 = async_fetch_data_1()
task2 = async_fetch_data_2()
task3 = async_fetch_data_3()
# Wait for all
results = await gather(task1, task2, task3)
# Total: max of wait times
JavaScript Async
Promises
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
Async/Await
async function getData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error(error);
}
}
Parallel Execution
// Sequential (slow)
const a = await fetchA();
const b = await fetchB();
// Parallel (fast)
const [a, b] = await Promise.all([fetchA(), fetchB()]);
Error Handling
// With try/catch
try {
const data = await riskyOperation();
} catch (error) {
handleError(error);
}
// With .catch()
riskyOperation()
.then(handleSuccess)
.catch(handleError);
Python Async
Async/Await
import asyncio
async def fetch_data():
await asyncio.sleep(1) # Simulate I/O
return "data"
async def main():
result = await fetch_data()
print(result)
asyncio.run(main())
Parallel Execution
async def main():
# Run concurrently
results = await asyncio.gather(
fetch_data_1(),
fetch_data_2(),
fetch_data_3()
)
return results
Timeouts
try:
result = await asyncio.wait_for(
slow_operation(),
timeout=5.0
)
except asyncio.TimeoutError:
print("Operation timed out")
Common Patterns
Sequential Processing
When order matters:
for (const item of items) {
await processItem(item);
}
Parallel Processing
When order doesn't matter:
await Promise.all(items.map(item => processItem(item)));
Batched Processing
For rate limiting:
const batchSize = 10;
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
await Promise.all(batch.map(processItem));
}
Race Condition
First result wins:
const result = await Promise.race([
fetchFromServer1(),
fetchFromServer2()
]);
Common Mistakes
Forgetting await
// Bug: doesn't wait
async function process() {
fetchData(); // Missing await!
useData(); // Data not ready
}
// Fixed
async function process() {
await fetchData();
useData();
}
Sequential When Parallel Works
// Slow
const a = await fetchA();
const b = await fetchB();
const c = await fetchC();
// Fast (if independent)
const [a, b, c] = await Promise.all([
fetchA(), fetchB(), fetchC()
]);
Unhandled Rejections
// Bad: unhandled rejection
asyncOperation();
// Good: handle errors
asyncOperation().catch(handleError);
// Or
try {
await asyncOperation();
} catch (e) {
handleError(e);
}
Async in Loops
// Wrong: doesn't wait
items.forEach(async (item) => {
await processItem(item); // Fires but doesn't wait
});
// Right: use for...of
for (const item of items) {
await processItem(item);
}
// Or parallel
await Promise.all(items.map(processItem));
When to Use Async
Good For
- I/O operations
- Network requests
- Database queries
- Multiple independent operations
- Non-blocking UI
Not Needed For
- CPU-intensive calculations
- Simple synchronous operations
- When order must be strictly sequential
Debugging Async
Async Stack Traces
Modern runtimes show async call stacks.
Logging
async function fetchData() {
console.log('Starting fetch');
const result = await api.get('/data');
console.log('Fetch complete');
return result;
}
Timing
console.time('operation');
await slowOperation();
console.timeEnd('operation');
Conclusion
Async programming enables:
- Non-blocking operations
- Parallel execution
- Better performance
- Responsive applications
Master async patterns for efficient code.
Next: Python Essentials - Python fundamentals for agents