Motivation for Asynchronous Programming
- Blocking I/O is problematic:
- Blocks entire thread
- I/O operations can be very slow
- Traditional solutions:
- Multiple threads (resource-heavy, see Concurrency and Multicore Programming.md)
- Non-blocking I/O with event loops (requires code restructuring, see Functional Programming for event-driven patterns)
- Goal: Overlap I/O and computation efficiently while maintaining code readability
Coroutines
-
Functions that can pause execution and yield values (see Functional Programming for generator/coroutine patterns)
-
Resume execution when needed
-
Enable cooperative multitasking
-
Example (Python):
def countdown(n): while n > 0: yield n n -= 1
Async/Await Pattern
-
asyncmarks functions that can yield during I/O -
awaitmarks points where function may yield control -
Runtime manages execution across multiple coroutines
-
Example (Rust, see Type Systems and Rust Programming Language.md):
async fn read_exact(mut reader: impl AsyncRead, buffer: &mut [u8]) -> Result<()> { let mut offset = 0; while offset < buffer.len() { match reader.read(&mut buffer[offset..]).await? { 0 => return Err(ErrorKind::UnexpectedEof.into()), n => offset += n, } } Ok(())}
Implementation Details
- Async functions compiled into state machines (see Type Systems and Rust Programming Language.md)
- In Rust, compiled to structs implementing
Futuretrait - Runtime schedules futures when I/O operations complete
Constraints and Limitations
- Must avoid blocking I/O operations (blocks entire runtime, see Concurrency and Multicore Programming.md)
- Must avoid long-running computations (starves other tasks)
- Can fragment ecosystem (libraries need async versions)
- Best for I/O-bound tasks, not compute-bound work