For people who know both systems well, how does this compare with Erlang? The await thing superficially looks a lot like Erlang waiting for a message from another process.
Await typically uses a CPS transform to rewrite methods into continuations. Effectively the remainder of the method after the await is passed as a callback to the async operation. The async operation can resume when it completes.
Erlang's waiting will interact with the VM's scheduler, and pause the green process until a message is available. Logically though, there's little difference between a stack treated as data by a scheduler, and the closure captured by the continuation after the CPS induced by await. The chief practical difference is a continuation could be called more than once. Resuming a green process with the same stack location more than once won't work very well because the stack gets reused. But you could get around that by storing the stack not as a reused vector, but as a linked list of heap-allocated activation records (an exact parallel to nested closures, which happens in anything beyond the most trivial CPS transform). Erlang wouldn't do that because (a) it's slower and (b) it's not necessary since it can't continue more than once.
(I implemented anonymous methods in the Delphi compiler and got intimately familiar with lots of these details.)
Generators are implemented with continuations, just like C#'s iterators (yield return statement). In C#'s case, it rewrites the function into a continuation via a state machine, but it's just using an integer to point at the remainder of the function, just like an index into an array isn't very different to a pointer - it's a distinction without much of a difference.
(JS VMs are free to implement generators however they like, of course.)