Async only solves one of these two cases.
I’d like to draw an analogy here to ⊥ “bottom” in Haskell. It’s used to represent a computation that does not return a value. Why doesn’t it return a value? Maybe because it throws an exception (and bubbles up the stack), or maybe because it’s in an infinite loop, or maybe it’s just in a very long computation that doesn’t terminate by the time the user gets frustrated and interrupts the program. From a certain perspective, sometimes you don’t care why ⊥ doesn’t return, you just care that it doesn’t return.
Same is often true for blocking calls. You often don’t care whether a call is slow because of I/O or whether it is slow because of a long-running computation. Often, you just care whether it is slow or how slow it is.
(And obviously, sometimes you do care about the difference. I just think that the “blocking code is a leaky abstraction” is irreparably faulty, as an argument.)