You are correct, await is a full replacement only for one case of yield usage. But the thing is you don’t need the other case any more. The old pattern where yield returned a function execution state object is now redundant and should now be replaced with awaiting signals. So the migration from yield is not always a trivial one.
Before, you could have a function, that contained a loop with logic, that shouldn’t be run every tick or every frame. E.g. a check that should be made every 10 seconds, or only when something happens. This could also be a function that returns different values upon each subsequent call, e.g. telling us how many of something we have left, or something like that. It was convenient to contain this loop in a separate function, a coroutine, i.e. a routine that could be thought of running at the same time with some main logic. It definitely ran periodically and thus definitely could be described with a loop and some “checkpoints”. Now, the thing about coroutine functions: they are called coroutines, because they know that they are going to be called from other places, and they know they are not simple functions. This is not a very intuitive pattern, in my opinion, especially for beginner game developers. But still, if you used such coroutines you would have another function (or functions) somewhere that called this coroutine function every time it/they needed. For example such a caller function would do it’s own business and then, say, wait 10 seconds, or calculate something, or just run a piece of game logic, and then periodically resume the coroutine. So the coroutine was simply a container of event handlers, that had to happen every once in a while, and the caller function was a “signal”, that called the coroutine and told it to execute the next handler. You could have just a collection of handler functions instead, but the coroutine could elegantly and conveniently express anything in a special place and even contain a loop to express some repetitiveness. You can see that I’ve put the signal word in quotation marks, because it has nothing to do with Godot signals, but one of the problems this coroutine pattern tried to solve was a similar problem signals do - connect a trigger with a handler.
Well, if coroutines and signals coexist solving similar problems (and can even meet and cooperate - the second usage of yield to wait for signals) then why not simplify everything and always use signals! That’s exactly what happened and what is now encouraged by Godot. If something has to happen every 10 seconds, we can still create something that emits signals every 10 second: a timer! Now, if we need a more sophisticated trigger, we have to find a way to express it with a signal. If before we used to call a coroutine continuation every time when, say, player jumped, then why not just declare a signal for it. And then we could just consume the events: e.g. create a “background” procedure that starts in _ready() and, in a loop, awaits the jumping signal and runs the handler logic in the same function! Or just awaits this signal from any place in the game code. Well, you know what signals are. Every time we called a coroutine before, we need to figure out what signal can we await to make it behave the same way.
This is a very nice exercise for a game developer: “how would I implement it with signals?”. “If it needs to happen every once in a while, what signals should I use/create?”. Coroutines are harder way to do it, that’s why, in my opinion, the developers got rid of yield
.