With the virtual thread model you have:
* No function coloring problem. This also means existing code is easier to port.
* possibility of transparent M:N scheduling.
* Impedence mismatch with OS primitives.
* Much more sophisticated runtime.
* Problematic task cancellation.
* Lots of care still needed for non-trivial inter-task synchronization.
With the async API model you have:
* Viral asyncification (the method color problem).
* Simpler runtime.
* Obvious and safe task cancellation.
* Completely orthogonal to parallelism (actually doing more than one thing simultaneously) for good and for bad.
* Inter-task coordination is straightforward and low-overhead even for sophisticated use cases.
* Higher initial learning curve.
I'm leaning toward liking the async approach more, but that might be just because I'm deep in the middle of it. I think the biggest argument in favor of virtual threads is the automatic parallelism; that's also the biggest argument against: free running threads require more expensive synchronization and introduce nondeterminism.