One Guard, Many Doors
I/O multiplexing lets one thread watch many sockets and only read the ones that are ready.
A guard who watches lights, not doors
Imagine one guard in a hallway with many doors. Bad plan: stand at Door A until someone knocks, even if Door B and Door C are already ringing. Better plan: watch a panel of lights. When a light turns on, go to that door.
Memorable label: “Watch readiness, not order.” I/O multiplexing is the light panel. It tells one thread which socket is ready now.
Six-year-old version: Do not stare at one toy box waiting for it to open. Watch all boxes. When any box pops open, play with that one.
Blocking waits in the wrong place
Last lesson got bytes to the right app using ports and sockets. Now we zoom inside the app. Suppose one server owns many sockets. If it reads socket A first and A has no data yet, the whole thread can block while socket B is already ready.
I/O multiplexing changes the question from “read this socket now” to “which sockets are ready?” Classic APIs such as select, poll, and epoll help programs wait for readiness across many file descriptors. Python’s select docs describe waiting until file descriptors are ready for I/O (Python select docs).
This is the bridge from networking to event loops. An event loop is a guard that keeps asking: “Which thing is ready next?”
Demo: blocking order vs readiness order
Run blocking mode first. Socket B is ready early, but waits behind slow socket A. Then run readiness mode. The app handles ready sockets first.
Readiness panel
Processing timeline
| Step | Time | Socket | Payload |
|---|---|---|---|
| No events yet. | |||
What to notice: readiness order lowers latency for sockets that are ready early.
How to recognize this problem
- One worker watches many I/O sources: sockets, files, pipes, timers.
- Waiting on one source can freeze others: slow client blocks fast client.
- You need low latency without one thread per connection: event loop shape.
- You hear select, poll, epoll, kqueue, or event loop: readiness multiplexing is nearby.
Exercise: run the I/O multiplexer model
Run:
node --test tests/io-multiplexer.test.mjs Then open src/io-multiplexer.mjs. The blocking model waits in socket list order. The readiness model sorts by readyAt, which is what the event-loop idea depends on.
Knowledge check
Interview payoff
Question: “How can one thread handle many network connections?”
Model answer: “It can use I/O multiplexing. Instead of blocking on one socket, the thread asks the OS which sockets are ready for reading or writing. Then it processes ready sockets and returns to waiting. This is the core idea behind event-driven servers and event loops.”
Pro tip: Non-blocking does not mean “no waiting.” It means waiting in the right place: one readiness wait for many sockets, not one blind wait per socket.
Next challenge: If readiness events arrive one by one, how does JavaScript turn them into callbacks, promises, and async/await?