-- http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...
The proper abstraction is threads--i.e. a stack structure used for the storage of temporary values, which is shared by nested function invocations as well as similar structured programming constructs like if/else conditionals that together represent a singular thread of control through the program as it processes data and events. "Thread of control" is precisely what you're cobbling together with promises, async/await, etc, using clunky compiler constructs bolted onto the language model and implementation. The problem is that we conflate the implementation with the abstraction, sometimes by necessity (the legacy of the contiguous, so-called "C stack") but usually unnecessarily.
It pays to remember that some operating systems, such as some of IBM's mainframe platforms, IIRC, implement a thread stack as a linked-list of invocation frames, rather than a contiguous array. But it's completely transparent, as it should be. Async/await does the exact same thing, except it's not transparent as it should be. So now you have _two_ distinct, mutually incompatible kinds of functions solely because the implementation is unable to properly hide the gory details, which is ludicrous on its face.