Synchronous vs Asynchronous: Control Flow and Notification Models
Synchronous vs Asynchronous
Synchronous means the caller waits for completion. Asynchronous means the caller continues immediately and gets notified later. This is orthogonal to blocking: you can have synchronous non-blocking (poll until ready) or asynchronous blocking (rare).
In async I/O, you submit a request and provide a callback or notification mechanism. The kernel performs the I/O in the background. When complete, it signals your code. You never explicitly wait for the result. The result comes to you.
Control Flow Differences
Synchronous: Code flows linearly. Call function, get result, use result. Easy to read and debug. Stack traces show the call chain. But the thread is blocked during I/O, unable to do other work.
Asynchronous: Code splits into request and handler. Submit request, do other work, handle result in callback. Harder to follow because logic is scattered. Stack traces show callback entry, not original request. But threads are never idle waiting.
Notification Models
Callbacks: Register a function to call when I/O completes. Simple but leads to callback hell with nested operations. Error handling scatters across callbacks.
Promises and Futures: Return a handle representing the eventual result. Chain operations with then() or await. Better than raw callbacks but still requires async thinking.
Coroutines: Write async code that looks synchronous. Await pauses the coroutine until result is ready. The runtime schedules other work meanwhile. Combines async efficiency with sync readability.