如何在Unity中的协程函数中使用await调用异步函数?

Art*_*rtS 3 c# unity-game-engine

我有一个协程如下:

IEnumerator CountingCoroutine()
{
    while (true)
    {
        DoSomething();
        yield return new WaitForSeconds(1);
    }
}
Run Code Online (Sandbox Code Playgroud)

我需要在其中调用一些异步函数。如果我按如下方式编写,编译器会抱怨:

async IEnumerator CountingCoroutine()
{
    while (true)
    {
        DoSomething();
        await foo();
        yield return new WaitForSeconds(1);
    }
}
Run Code Online (Sandbox Code Playgroud)

每秒调用一些异步函数的正确等待时间是多少?PS我可以放弃整个协程函数并使用带有计时机制的更新,并将更新函数标记为异步。但我不确定它是否安全或推荐。

根据 @derHugo 的建议解决方案:

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

der*_*ugo 6

你可以使用Task.IsCompleted

喜欢

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

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

一旦之前的任务完成,它就会开始运行新的任务。

您可以扩展它,以便最早大约每秒开始一个新任务,或者最晚在前一个任务在一秒后完成时开始,例如

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,如果您不关心完成情况,只想像每秒所说的那样调用它,那么只需这样做

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

        Task.Run(foo);
        yield return new WaitForSeconds(1);
    }
}
Run Code Online (Sandbox Code Playgroud)

但请注意,这样您将失去所有控制,并且永远不会知道其中一项任务是否以及何时完成,并且可能会导致多个并发任务运行/失败等


作为替代方案,您也可以反过来使用Asyncoroutine之类的东西。不过,您可能必须注意后台线程上的事物。


更新

现在我宁愿建议尝试一下UniTask(也可以通过OpenUPM获得)!它是一个轻量级的异步任务实现,还提供无缝的异步 <-> Unity 主线程集成。