What is the equivalent of yield return null;
in Coroutine (that run each frame at Update) in an async method ?
The nearest I got to find is await Task.Delay(1);
, but it DO NOT run every frame.
private IEnumerator RunEachFrame()
{
while (true)
{
print("Run Each frame right before rendering");
yield return null;
}
}
async void DoNotRunEachFrame()
{
while (true)
{
await Task.Delay(1); // What is the equivalent of yield return null here ?
}
}
There is currently no equivalent method for for yield return null
.
I was going to say it's not possible since async can be called in another Thread
other than the main Thread
which can throw exceptions since you can't use Unity's API in another Thread but it looks like Unity fixed the Thread issue by implementing their own async context in Unity 5.6.0b5 and above.
It' still possible to do but you have to implement it yourself or use an existing API. The UnityAsync
API can do this already. You can get it here. The NextUpdate
function replaces the yield return null
instruction.
Examples:
Your usual coroutine code:
private IEnumerator RunEachFrame()
{
while (true)
{
print("Run Each frame right before rendering");
yield return null;
}
}
The equivalent async code:
using UnityAsync;
using System.Threading.Tasks;
public class UpdateLoop : AsyncBehaviour
{
void Start()
{
RunEachFrame();
}
// IEnumerator replaced with async void
async void RunEachFrame()
{
while(true)
{
print("Run Each frame right before rendering");
//yield return null replaced with await NextUpdate()
await NextUpdate();
}
}
}
Notice how the script inherits from AsyncBehaviour
instead of MonoBehaviour
.
If you really want to inherit from MonoBehaviour
instead of AsyncBehaviour
and still use this API, call the NextUpdate
function directly as Await.NextUpdate()
.Here is a complete equivalent example:
using UnityAsync;
using System.Threading.Tasks;
public class UpdateLoop : MonoBehaviour
{
async void Start()
{
await RunEachFrame();
}
async Task RunEachFrame()
{
while(true)
{
print("Run Each frame right before rendering");
await Await.NextUpdate(); // equivalent of AsyncBehaviour's NextUpdate
}
}
}
Below are the complete supported wait functions:
NextUpdate
NextLateUpdate
NextFixedUpdate
Updates(int framesToWait)
LateUpdates(int framesToWait)
FixedUpdates(int stepsToWait)
Seconds(float secondsToWait)
SecondsUnscaled(float secondsToWait)
Until(Func<bool> condition)
While(Func<bool> condition)
Custom(CustomYieldInstruction instruction)
AsyncOp(AsyncOperation op)
All these can be found in the Await
class just in-case they get renamed or removed.
If you ever run into issues with this API, see Unity's forum post dedicated to it and ask questions there.
At least in Unity 2018 you can use await Task.Yield()
. For example:
using System.Threading.Tasks;
using UnityEngine;
public class AsyncYieldTest : MonoBehaviour
{
async void Start()
{
await Function();
}
async Task Function() {
while (gameObject != null)
{
await Task.Yield();
Debug.Log("Frame: " + Time.frameCount);
}
}
}
will give you output:
Frame: 1
Frame: 2
Frame: 3
...
It seems that if the Debug.Log("Frame: " + Time.frameCount);
line was before await Task.Yield();
, it would run twice during the first frame. I'm not sure what's the reason for that.
With UniTask library's UniTask.NextFrame
it's possible to get behaviour that matches yield return null
fully so that you don't get 2 messages on the first frame with
using Cysharp.Threading.Tasks;
using UnityEngine;
public class AsyncYieldTest : MonoBehaviour
{
async void Start()
{
await Function();
}
async UniTask Function() {
while (gameObject != null)
{
// Debug.Log first like with yield return null
Debug.Log("Frame: " + Time.frameCount);
await UniTask.NextFrame();
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With