How is async/await implemented under the hood?
Instead it is based on "wakers", which are objects associated with a task, and can be used to notify the task's executor that the task is ready to make progress. It is then the job of an executor resume the task. So there is an extra layer of indirection (conceptually).
There are pros and cons, but in essence the system trades a check on resume (often redundant) for the need to make a heap allocation and/or type erasure at each await point.
(It's possible to avoid the latter in continuation-based implementations, like C++ coroutines, but it's pretty hard.)