Non-blocking I/O: "O_NONBLOCK" with "EWOULDBLOCK" (or "EAGAIN")
Signal-driven I/O: "O_ASYNC" with "SIGIO"
Synchronous I/O multiplexing: "select()"
All 3 had various defects, especially with a large number of concurrent I/O actions.
In UNIX-derived operating systems, after 1983 there have been many other attempts to implement something better than these 3 (starting with System V "poll" and with POSIX AIO, and then with various incompatible approaches in Solaris, FreeBSD and Linux), but none were good enough and most were seriously inferior to methods of doing asynchronous I/O that existed in some IBM and DEC operating systems decades earlier.
In my opinion, only io_uring has finally solved the problem of multiplex asynchronous I/O in Linux, and in a manner much better than in all older operating systems. I consider all the many older alternatives that exist for liburing as obsolete.