> the async IO that happens is done via an internal thread-pool
Not quite, since using threads internally (thread per IO operation) would defeat a purpose of async system, which is created specifically to not pay threading cost (context switches, etc.). In layman terms, kernel will simply invoke your C callback function when "stuff happens", and libuv propagates that to JS in platform-agnostic manner.
There are still threads and thread pools in V8 and node for stuff like calculating PI (or, more realistically, password hashing and other number crunching), but that is mostly unrelated to async IO itself.
> using threads internally (thread per IO operation) would defeat a purpose of async system
Yes and no.
Having single threaded JS (no matter if runtime uses epoll) allows for JS concurrency concerns to be limited to a small number of places, i.e
no pre-emptive. This makes concurrency in JS easier to reason about.
Not quite, since using threads internally (thread per IO operation) would defeat a purpose of async system, which is created specifically to not pay threading cost (context switches, etc.). In layman terms, kernel will simply invoke your C callback function when "stuff happens", and libuv propagates that to JS in platform-agnostic manner.
There are still threads and thread pools in V8 and node for stuff like calculating PI (or, more realistically, password hashing and other number crunching), but that is mostly unrelated to async IO itself.
Some google queries: select, poll, epoll, kqueue…