Knowing When A Coroutine Finishes

Did you know that a coroutine can yield until another coroutine finishes? I didn’t. Let’s talk about it and why it’s useful.

Backstory

A few weeks back, I was working with some 3rd party code that heavily used coroutines. In one place it had a “chain” of coroutines that each called another coroutine sometimes multiple coroutines and this went 5-6 coroutines deep.

First. Yuck.

Second. Holy cow.

For what I was working on, I needed to know when the process (all the coroutines) had finished. The “usual” and often suggested method on the interwebs is to add a class-wide boolean to track when the coroutine is complete - you set it to true when you start and set it to false when you finish.

I’ve never liked this approach, but sometimes it’s good enough.

In the case that I was working with, the boolean approach just wasn’t practical or at least was going to be extra icky. And I definitely wasn’t going to add a boolean parameter to each coroutine and try to pass it through… No chance.

So I got to wondering if there was a better way. It turns out there is. And somehow I’d missed it up until now.

Ironically, if I’d looked closer at the coroutines in the 3rd party code I would have seen the solution in action. Oops… You win some you lose some.

A Better Way

So this may sound like just passing the buck. BUT! You can have a coroutine yield until another coroutine is finished.

(Seriously, how did I not know about this?)

This means in my case, a cluster of sequential coroutines, I can create yet another coroutine and have it yield until the cluster finishes it’s business. So if the coroutines exit early and DON’T make it to the end of the chain - for some reason. I’ll still know that the coroutines are done. I may not know why, but I know it’s done. And that’s a hugely useful thing!

Plus!

We can throw in a function to call when the coroutines have done their business. Frankly, this is often what we really want to do - wait until the coroutine is done and then run some other code.

In my opinion, this works and it works well. But I think it also opens the doors to better code structure (assuming it’s your code and you can change it).

Better Still!

A chain of coroutines makes it hard to debug and in my opinion and makes harder it than necessary to follow the flow of the code. So with what we know now, or at least with what I know now, we could restructure the code and make it easier to read.

So instead of one coroutine calling the next coroutine, I can call them all, sequentially, from within a single wrapping or master coroutine. This avoids the hard to follow “chain” AND it avoids a huge monolithic coroutine (i.e. just making it all one coroutine).

In fact, I used this exact approach in my current personal project where I had several actions that needed to happen sequentially, but also over a controlled period of time. Launching a missile requires a lot of moving pieces - it is actually rocket science!

Launching an ICBM from a missile SILO


Next Level

Coroutine with callback

But we can go one step further!

If you’ve spent any time on my channel or discord you know I love actions and events. I rarely pass up a chance to use them and this is no different!

Rather than have a set function called when the coroutines finish, we can pass in an action that will act as a callback.

Meaning the same coroutine can run, but with a different reaction when it’s finished. This can be super useful if the coroutine is public (yes, coroutines can be started from other classes) or if it is somehow getting invoked by different objects or for different reasons.

I generally don’t love making coroutines public, not sure why, just don’t. But it’s easy enough to add a public function.

Passing in a callback function

Either way, passing in an action (i.e. function) is easy and makes for very useful code and potentially reusable code.