OS & Systems FundamentalsI/O Models (Blocking, Non-blocking, Async)Medium⏱️ ~3 min

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.

⚠️ Key Trade-off: Async improves throughput but complicates code. Debugging async code is harder: stack traces do not show causation. Error handling must propagate through callbacks or promise chains. Use async when concurrency demands justify the complexity.
💡 Key Takeaways
Synchronous waits for completion; asynchronous continues and gets notified later
Sync code flows linearly with clear stack traces; async scatters logic into callbacks
Callbacks lead to nesting; promises and futures enable chaining; coroutines look synchronous
Async improves throughput because threads do not wait idle for I/O
Debugging async is harder: stack traces show handler not original request
📌 Interview Tips
1Distinguish blocking versus async: blocking is about thread sleep, async is about notification model. They are orthogonal concepts
2Explain async debugging challenge: when a callback fails, the stack trace shows the event loop, not the code that initiated the request
3Recommend coroutines (async and await) as middle ground: async efficiency with synchronous-looking code
← Back to I/O Models (Blocking, Non-blocking, Async) Overview