How to call async function with await within coroutine function in Unity?

Viewed 5493

I have a coroutine as follows:

IEnumerator CountingCoroutine()
{
    while (true)
    {
        DoSomething();
        yield return new WaitForSeconds(1);
    }
}

I need to call some async function within it. If I wrote it as follows, the compiler complains:

async IEnumerator CountingCoroutine()
{
    while (true)
    {
        DoSomething();
        await foo();
        yield return new WaitForSeconds(1);
    }
}

What is the proper wait to call some async functions per second? P.S. I could abandon the whole coroutine function and use update with time counting mechanism, and mark the update function as async. But I'm not sure if it's safe or recommended.

Solution with recommendations from @derHugo:

IEnumerator CountingCoroutine()
{
    while (true)
    {
        DoSomething();
        var task = Task.Run(foo);
        yield return new WaitUntil(()=> task.IsCompleted);
        yield return new WaitForSeconds(1);
    }
}
1 Answers

You could use the Task.IsCompleted

like

private IEnumerator CountingCoroutine()
{
    while (true)
    {
        DoSomething();

        var task = Task.Run(foo);
        yield return new WaitUntil(()=> task.IsCompleted);
    }
}

which starts running a new Task as soon as the one before has finished.

You could extend this in order to start a new task earliest about every second or latest when the previous task finishes after a second like e.g.

private IEnumerator CountingCoroutine()
{
    var stopWatch = new StopWatch();
    while (true)
    {
        DoSomething();
        stopWatch.Restart();
        var task = Task.Run(foo);
        yield return new WaitUntil(()=> task.IsCompleted);

        var delay = Mathf.Max(0, 1 - stopWatch.ElapsedMilliseconds * 1000);

        // Delays between 1 frame and 1 second .. but only the rest that foo didn't eat up
        yield return new WaitForSeconds(delay);

        // or you could with a bit more calculation effort also 
        // even avoid the one skipped frame completely
        if(!Mathf.Approximately(0, delay)) yield return new WaitForSeconds(delay);
    }
}

Or if you don't care about the finishing and only want to call this as you say every second then simply do

private IEnumerator CountingCoroutine()
{
    while (true)
    {
        DoSomething();

        Task.Run(foo);
        yield return new WaitForSeconds(1);
    }
}

Note however that this way you lose all control and will never know if and when one of these tasks has finished and might get multiple concurrent tasks running/failing etc


As alternative you could probably also go the other way round and use something like Asyncoroutine) .. you might have to watch out for things landing on a background thread though.

Related