NT's IO model gives you asynchronous wait on arbitrary event sources, including file IO — not just the network stack. It's actually quite nice if you get over the hideous type names and verbose CamelCase symbols.
In contrast, select(), from 4.2BSD through the current day, cannot be used for async IO on regular files. It simply returns immediately. If used naively this could result in a busy loop, I guess, although getting stuck in a blocking disk operation seems more likely to me.
kqueue cannot be used for async IO on files, either. Same problem.
EVFILT_READ Takes a descriptor as the identifier, and returns
whenever there is data available to read. The
behavior of the filter is slightly different
depending on the descriptor type.
Sockets
[Non-listen] socket descriptors return when there is
data to be read, subject to the SO_RCVLOWAT value
of the socket buffer.
Vnodes
Returns when the file pointer is not at the end
of file. data contains the offset from current
position to end of file, and may be negative.
There's aio(4), but support and APIs vary wildly across platforms.
The Unix async model based around fds and select was really only designed for sockets, and has been extended to socket-like objects (pipes/fifos). Linux has added support for some other sources in the form of e.g. signalfd(), eventfd(), timerfd_create(); the BSDs just added it all to kqueue/kevent. Neither supports async file operations in the fd model (basic operations like read/write; nor more complicated operations like syncing a range, renaming a file, creating a file, etc).
Both async I/O and select/poll are ways of multiplexing concurrent I/O activities onto a single thread of control. Both provide a way to initiate I/O and wait for completion without busy waiting. Both require timed-out waits if the thread must give periodic attention to something other than I/O.