
Libuv
4/14/2025
Libuv is a C library initially developed for Node.js to handle non blocking I/O (among other things).
Non blocking I/O you say ?
Imagine you want to implement a new cool web server, in a single threaded world.
Your server needs to handle hundreds or thousands requests as fast as possible, but every time you handle a request, no one can actually connect to your server, meh.
By using libuv, you say “Hey, I want to watch connections/requests on a socket, please let me know when something happens and do some work."
"Some work” must be fast enough so that next requests can be handled. If you take too much time here you are blocking the event loop (more on this later).
In the middle of “some work”, you might want to send a network request, write to a file… which could block everything: not good.
With libuv you can do this asynchronously so it won’t block your work. Network I/O is handled with async APIs provided by your OS while filesystem I/O will be handled by a pool of threads.
What the loop ?
Libuv implements an event loop, which means you can give work to it (handles and requests) and be notified when the job is done, or an event happened like a new tcp connection.
Two fundamental concepts are involved here:
Handles
Handles are long-lived objects like timers, tcp server, etc.
Requests
Requests are one-shot async operations, like read/write to a file, send a DNS request, etc.
On callback execution, you can schedule more work to do, the loop will end when there is no more work.
Except for work done in the libuv thread pool, everything is done on the main thread. It’s important to remember because while the loop executes your code, nothing else is running aside.
Some poorly drawn schema about the loop initialization and running operation:

As you can see, when your callbacks get exec’d, control flow is out of the loop. Here you could add more work for the loop.
Then your code gives back the control to the loop, allowing it to process other events.
This cycle will repeat indefinitely, usually waiting in the poll I/O phase. Let’s talk about the different loop’s phases.
Event loop phases

Phases group callback executions per “type”.
Prepare/check phases
Prepare/Check phases are used to do work before/after I/O polling.
In Node.js, the check phase is used to trigger setImmediate callbacks.
I/O poll phase
I/O polling phase can block for a certain amount of time, usually the closest timer to expire, or indefinitely, if no check/close callbacks/timers are registered. This phase triggers the related I/O callbacks.
Close callbacks phase
Triggers callbacks related to files closed, streams closed, etc.
Timers phase
The loop has it’s own representation of “now”, which is updated before running due timers.
We’ve seen many concepts here but if you want to dive deeper, the best resource is probably the official documentation.