0. it plays hell with threadlocals-as-dynamic-scoping, which is the only way most "modern" languages permit dynamically scoped variables
1. it needs to be correctly passed along to callers, and given it's usually used in dynamically typed languages there's a high risk of forgetting and dropping a yield
2. yield being also used for iteration, it can be confusing to keep them straight.
It's definitely a better solution than callback hell though. An other approach is runtime support as in gevent, where the "yielding" is done by the library/IO code and invisible to the caller. The final two I know of are baking lightweight concurrency and communications into the language itself (Erlang, and to a lower extent Go and Rust) or monadic systems (Haskell)