The key difference is that goto
statements in languages that support them allow jumping to any location in the program with little or no restriction. While coroutines may on the surface seem similar they are very different.
Coroutines allow procedures to be suspended (with all their context) and resumed at certain locations. So while coroutines do pause and yield
control to other procedures before they complete and then resume later, the points at which the procedures yield and resume from is known ahead of time.
It is not possible to simply jump to an arbitrary line in a procedure, the procedure in question has to waiting to be resumed at a specific location. While this passing of control is much more structured than with goto
it is possible to write confusing code by overusing this powerful mechanism. Then again that is that not the case with every powerful programming language feature? ;-)