Not only do threads not really do the thing (in part because of the high cost of threads compared to alternatives), the alternative isn't only threads. Users of Lua, Greenlet, ucontext_t, libco, etc. have been able to write single-threaded code that's generic over the async-ness of its callees for decades. Recently there's a trend toward preferring needing to change 99 call sites and function signatures when adding a single piece of I/O to a single function though, needing to write two versions of each library, etc.