&gt; programs that use lots of signals and&#x2F;or asynchronous completion of IO or timers (&quot;completions&quot;)<p>This is actually what the most basic event loop handles for you. OS signals, timers and asynchronous events are not delivered into the middle of your program, but into the point, where all of the event handlers are not running. That&#x27;s the beauty of it. You can then react to the event and cancel whatever you are waiting to be running, or schedule something new.<p>With coroutines you can only safely handle those things each in a separate coroutine. But you don&#x27;t need them isolated, you need your code to actually do something on those signals and timer events. So, you end up with a lot of flags spread all over other coroutines, protected by mutexes&#x2F;channels. But then it&#x27;s still not responsive, because you cannot unblock a currently blocking call, so many concurrency patterns do not apply and even bad things happen, like leaks and deadlocks. You rethink it again and move blocking calls each into a separate coroutine, so you would be able to construct a loop that waits for messages from other coroutines and reacts right away, abandoning blocking coroutines, if required. Hopefully you eventually realize, that you are emulating an event loop, but a very clumsy one, and go back to working with event handlers and higher-order functions.