Asynchronous Patterns
Modern non-blocking concurrency with Futures and async/await.
Introduction: Futures and Promises
Section titled “Introduction: Futures and Promises”Futures and Promises represent values that don’t exist yet, enabling asynchronous computation without blocking threads.
Visual: Future Concept
Section titled “Visual: Future Concept”Blocking vs Non-Blocking I/O
Section titled “Blocking vs Non-Blocking I/O”Understanding the difference is crucial!
Visual: Blocking vs Non-Blocking
Section titled “Visual: Blocking vs Non-Blocking”Java: CompletableFuture
Section titled “Java: CompletableFuture”CompletableFuture is Java’s modern way to handle asynchronous operations.
Creating CompletableFutures
Section titled “Creating CompletableFutures”1import java.util.concurrent.CompletableFuture;2import java.util.concurrent.ExecutionException;3
4public class CompletableFutureBasics {5 public static void main(String[] args) throws ExecutionException, InterruptedException {6 // Create with supplyAsync (returns value)7 CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {8 try {9 Thread.sleep(1000);10 } catch (InterruptedException e) {11 Thread.currentThread().interrupt();12 }13 return "Hello";14 });15
16 // Create with runAsync (no return value)17 CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {18 System.out.println("Running async task");19 });20
21 // Get result (blocks until ready)22 String result = future1.get();23 System.out.println("Result: " + result);24 }25}Chaining Operations
Section titled “Chaining Operations”thenApply vs thenCompose
Section titled “thenApply vs thenCompose”thenApply: Transforms result synchronously (returns value)thenCompose: Chains another CompletableFuture (returns Future)
Visual: thenApply vs thenCompose
Section titled “Visual: thenApply vs thenCompose”Example: Chaining Operations
Section titled “Example: Chaining Operations”1import java.util.concurrent.CompletableFuture;2
3public class CompletableFutureChaining {4 public static void main(String[] args) {5 // Chain operations6 CompletableFuture<String> future = CompletableFuture7 .supplyAsync(() -> "Hello")8 .thenApply(s -> s + " World") // Synchronous transformation9 .thenApply(String::toUpperCase); // Another transformation10
11 future.thenAccept(System.out::println); // Consume result12
13 // thenCompose for async chaining14 CompletableFuture<String> future2 = CompletableFuture15 .supplyAsync(() -> "user123")16 .thenCompose(userId -> fetchUserData(userId)); // Returns Future17
18 // Exception handling19 CompletableFuture<String> future3 = CompletableFuture20 .supplyAsync(() -> {21 if (Math.random() > 0.5) {22 throw new RuntimeException("Error!");23 }24 return "Success";25 })26 .exceptionally(ex -> "Handled: " + ex.getMessage()) // Handle exception27 .thenApply(s -> "Result: " + s);28 }29
30 static CompletableFuture<String> fetchUserData(String userId) {31 return CompletableFuture.supplyAsync(() -> {32 // Simulate async fetch33 return "Data for " + userId;34 });35 }36}Combining Futures
Section titled “Combining Futures”allOf: Wait for all futures to completeanyOf: Wait for any future to complete
1import java.util.concurrent.CompletableFuture;2import java.util.Arrays;3
4public class CombiningFutures {5 public static void main(String[] args) {6 // Create multiple futures7 CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Result 1");8 CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Result 2");9 CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "Result 3");10
11 // Wait for all12 CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2, future3);13 allFutures.thenRun(() -> {14 System.out.println("All completed!");15 });16
17 // Wait for any18 CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(future1, future2, future3);19 anyFuture.thenAccept(result -> {20 System.out.println("First result: " + result);21 });22
23 // Combine two futures24 CompletableFuture<String> combined = future1.thenCombine(future2,25 (r1, r2) -> r1 + " + " + r2);26 }27}Python: asyncio
Section titled “Python: asyncio”Python’s asyncio provides async/await syntax for asynchronous programming.
Event Loop
Section titled “Event Loop”The event loop manages and executes asynchronous tasks.
Visual: Event Loop
Section titled “Visual: Event Loop”Example: Basic asyncio
Section titled “Example: Basic asyncio”1import asyncio2
3async def fetch_data(url):4 """Async function (coroutine)"""5 await asyncio.sleep(1) # Simulate I/O6 return f"Data from {url}"7
8async def main():9 # Run coroutines concurrently10 results = await asyncio.gather(11 fetch_data("url1"),12 fetch_data("url2"),13 fetch_data("url3")14 )15 print(results)16
17# Run event loop18asyncio.run(main())concurrent.futures.Future vs asyncio.Future
Section titled “concurrent.futures.Future vs asyncio.Future”1from concurrent.futures import ThreadPoolExecutor, Future as ThreadFuture2import asyncio3
4# ThreadPoolExecutor Future5def sync_task():6 return "Result"7
8with ThreadPoolExecutor() as executor:9 thread_future = executor.submit(sync_task)10 result = thread_future.result() # Blocks11
12# asyncio Future13async def async_task():14 await asyncio.sleep(1)15 return "Result"16
17async def main():18 asyncio_future = asyncio.create_task(async_task())19 result = await asyncio_future # Non-blocking20 print(result)21
22asyncio.run(main())Cross-Language Comparison
Section titled “Cross-Language Comparison”| Feature | Java | Python |
|---|---|---|
| Future Creation | CompletableFuture.supplyAsync() | asyncio.create_task() or executor.submit() |
| Chaining | thenApply(), thenCompose() | await in coroutines |
| Combining | allOf(), anyOf() | asyncio.gather(), asyncio.wait() |
| Exception Handling | exceptionally(), handle() | try/except in coroutines |
| Event Loop | Implicit (ForkJoinPool) | Explicit (asyncio.run()) |
Real-World Example: Async API Client
Section titled “Real-World Example: Async API Client”1import java.util.concurrent.CompletableFuture;2import java.util.List;3import java.util.stream.Collectors;4
5public class AsyncAPIClient {6 public CompletableFuture<String> fetchUser(String userId) {7 return CompletableFuture.supplyAsync(() -> {8 // Simulate API call9 try {10 Thread.sleep(100);11 } catch (InterruptedException e) {12 Thread.currentThread().interrupt();13 }14 return "User: " + userId;15 });16 }17
18 public CompletableFuture<List<String>> fetchUsers(List<String> userIds) {19 List<CompletableFuture<String>> futures = userIds.stream()20 .map(this::fetchUser)21 .collect(Collectors.toList());22
23 return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))24 .thenApply(v -> futures.stream()25 .map(CompletableFuture::join)26 .collect(Collectors.toList()));27 }28}1import asyncio2
3class AsyncAPIClient:4 async def fetch_user(self, user_id):5 """Simulate async API call"""6 await asyncio.sleep(0.1)7 return f"User: {user_id}"8
9 async def fetch_users(self, user_ids):10 """Fetch multiple users concurrently"""11 tasks = [self.fetch_user(uid) for uid in user_ids]12 return await asyncio.gather(*tasks)13
14# Usage15async def main():16 client = AsyncAPIClient()17 users = await client.fetch_users(["user1", "user2", "user3"])18 print(users)19
20asyncio.run(main())Interview Questions
Section titled “Interview Questions”Q1: “What’s the difference between thenApply and thenCompose?”
Section titled “Q1: “What’s the difference between thenApply and thenCompose?””Answer:
thenApply: Synchronous transformation, takes value, returns valuethenCompose: Asynchronous chaining, takes value, returns Future- Use
thenApply: For simple transformations - Use
thenCompose: When you need to chain another async operation
Q2: “When would you use async/await vs threads in Python?”
Section titled “Q2: “When would you use async/await vs threads in Python?””Answer:
- async/await: I/O-bound concurrent operations, many connections, event-driven
- threads: CPU-bound tasks, simpler I/O scenarios, when you need OS-level parallelism
- Choose: Based on task type and concurrency requirements
Key Takeaways
Section titled “Key Takeaways”Next Steps
Section titled “Next Steps”- Lock-Free Programming - CAS and atomic operations
- Concurrency Hazards - Deadlocks and race conditions
Mastering asynchronous patterns enables efficient non-blocking systems! ⚡