CoroutineYieldInstruction and Tasks

Unity has this neat way to Create your own CustomYieldInstructions, i.e. ways to implement your own WaitForSeconds or WaitUntil style functions.

You can take the code from my previous post:

private IEnumerator CoroutineWithTasks()
{
    Task task1 = Task.Factory.StartNew(SomeWork1);
    Task task2 = Task.Factory.StartNew(SomeWork2);
    
    bool TasksFinished()
    {
        bool task1Complete = task1.IsCompleted && task1.Status == 
         TaskStatus.RanToCompletion;
        bool task2Complete  = task2.IsCompleted && task2.Status == 
         TaskStatus.RanToCompletion;
    }
    
    var waitUntil = new WaitUntil(TasksFinished);
    yield return waitUntil;
    DoSomeMoreCode();
}

And turn it into this:

private IEnumerator CoroutineWithTasks()
{
    Task task1 = Task.Factory.StartNew(SomeWork1);
    Task task2 = Task.Factory.StartNew(SomeWork2);

    var waitUntil = new WaitForTasks(task1, task2);
    yield return waitUntil;
    DoSomeMoreCode();
}

You will notice the new WaitForTasks line. This is our custom yield code:

using System.Threading.Tasks;
using UnityEngine;

public class WaitForTasks : CustomYieldInstruction
{
    private readonly Task[] _tasks;
    
    public WaitForTasks(params Task[] tasks)
    {
        _tasks = tasks;
    }

    public override bool keepWaiting
    {
        get
        {
            int count = _tasks.Length;
            for (int i = 0; i < count; ++i)
            {
                var task = _tasks[i];
                bool taskComplete = task.IsCompleted && task.Status == 
                       TaskStatus.RanToCompletion;
                if (!taskComplete)
                {
                    return true;
                }
            }
            return false;
        }
    }
}

We just pass tasks in, iterate over them to see if they are finished and keep waiting until they are done. Simple!