Coroutines: Starting, Stopping, Interrupting, IsRunning ...

I’ve been writing lots of coroutines lately. A common functionality is that I need stop an existing coroutine before starting it (if it’s already running). Another common functionality is that I need to be able to see if a given coroutine is running. Typically this is how I accomplish this:

Coroutine myCoroutine;

void Begin() {
  if (myCoroutine != null) {
    StopCoroutine(myCoroutine);
  }

  myCoroutine = StartCoroutine(MyCoroutine());
}

void End() {
  if (myCoroutine != null) {
    StopCoroutine(myCoroutine);
    myCoroutine = null;
  }
}

bool IsRunning() {
  return myCoroutine != null;
}

IEnumerator MyCoroutine() {
  yield return new WaitForSeconds(1);
  // .. do something
  myCoroutine = null;
}

For a class that has a few different coroutines this starts to get a little ugly. Is there a better way to do this? I guess I write a custom class for this.

What I like to do is have a manager that handles the actual coroutines. It allows you to actually start, stop, and keep track of things that are running… for example:

public class CoroutineManager : MonoBehaviour
{
    private static CoroutineManager Instance
    {
        get
        {
            if (mInstance == null)
            {
                mInstance = new GameObject("CoroutineManager").AddComponent<CoroutineManager>();
                GameObject.DontDestroyOnLoad(mInstance.gameObject);
            }
            return mInstance;
        }
    }
    private static CoroutineManager mInstance;
    private static Dictionary<string, Coroutine> runningRoutines = new Dictionary<string, Coroutine>();
    private static IEnumerator InternalRoutine(string key, IEnumerator routine)
    {
        yield return routine;
        runningRoutines.Remove(key);
    }

    public static void Start(string key, IEnumerator routine)
    {
        Stop(key);
        var internalRoutine = Instance.StartCoroutine(InternalRoutine(key, routine));
        runningRoutines.Add(key, internalRoutine);
    }
    public static bool IsRunning(string key)
    {
        return runningRoutines.ContainsKey(key);
    }
    public static bool Stop(string key)
    {
        Coroutine routine;
        if (runningRoutines.TryGetValue(key, out routine))
        {
            Instance.StopCoroutine(routine);
            runningRoutines.Remove(key);
            return true;
        }
        return false;
    }
}

Then you can use it like this

public class ExampleScript : MonoBehaviour
{
    private const string KEY = "TEST";

    private void Start()
    {
        CoroutineManager.Start(KEY, TestRoutine());
    }
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            if (!CoroutineManager.IsRunning(KEY))
            {
                Debug.LogFormat("Routine {0} is not running... Starting it", KEY);
                CoroutineManager.Start(KEY, TestRoutine());
            }
        }
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            if (CoroutineManager.Stop(KEY))
            {
                Debug.LogFormat("Stopped Routine {0}", KEY);
            }
        }
    }
    private IEnumerator TestRoutine()
    {
        Debug.Log("Hello");
        yield return new WaitForSeconds(5);
        Debug.Log("World");
    }
}
2 Likes