#Coroutine Best Practice: WaitUntil vs. While-loop yield return null

1 messages · Page 1 of 1 (latest)

mortal pawn
#

Two questions:

  1. In coroutines, is there a performance difference between the following two? Which one is more performant?
// Code before

while(!IsFoo())
{
    yield return null; 
}

// Code after

versus

// Code before

yield return new WaitUntil(IsFoo);

// Code after
  1. Are there any potential problems with caching WaitUntil as you would with WaitForSeconds? For example, in the field declaration?

Thanks!

spark widget
#

WaitUntil and its friends all involve a small allocation, i think they're safe to cache but it's also pretty insignificant compared to the general coroutine overhead

#

looping and yielding null does more or less the same thing and doesn't allocate, the only downside is it's more verbose

woven hare
granite pewter
#

I'm pretty sure WaitUntil exists simply because it's convenient to use. I personally prefer the above solution, I think the readability of it is fine. The above solution also lets you do something like this:

while(true) {
  // do stuff
  if(someCondition)
    yield return null;
  else if(someOtherCondition)
    yield break;
}
``` Which I don't think is specifically achievable with `WaitUntil` (although you could probably make something very similar).
woven hare
granite pewter
woven hare
#

Why would you do that?

granite pewter
granite pewter
woven hare
woven hare
granite pewter
#

I don't think the question should be "why would you do that" but rather "what situation would you do that in". I'm not making any claims that they should do this, I'm saying it allows you to do thinks like that.

spark widget
#

the other thing worth mentioning is that using WaitUntil means you always yield at least one frame afaik, if you have a while/yield null loop, you may not need to yield at all if your condition can sometimes be true from the start

#

like loading a resource that might be cached for example

granite pewter
#

WaitUntil can also take in a TimeSpan timeout, an Action onTimeout, and a WaitTimeoutMode timeoutMode along with the predicate, so it lets you have timeouts easily while you wait for something to finish without programming it yourself.

woven hare
granite pewter
#

Straight from the Unity 6000.0.4f1 C# reference source code

#

not all constructors are listed in the unity documentation

#

there are many undocumented constructors and methods

woven hare
granite pewter
#

wtf do you mean could I please show a code example? I showed you a screenshot from unity's c# source code

woven hare
#

That's why I'm asking for its usage

#

How would you return it?

granite pewter
#

what?

woven hare
#

How do you use it in a coroutine, I'm asking

granite pewter
#

the same way you use any CustomYieldInstruction

woven hare
#

Not the same way. You cannot use it.

granite pewter
#

why not?

woven hare
#

That's why I'm asking you for a code example

granite pewter
#

alright let me boot up unity and figure it out for you. I was just looking at the c# reference, there's a public constructor there so unity is allowing us to construct the object in that way

granite pewter
granite pewter
# woven hare That's why I'm asking you for a code example

Since you do not answer, I assume you said "you cannot use it" for no reason which is very strange. I was able to use it fine. In fact, looking at the documentation, it does list the parameters in the constructor under the single-parameter constructor (although it lists the wrong parameter type for onTimeout, in reality it is System.Action not whatever that is).

using System;
using System.Collections;

using UnityEngine;

public class TestYield : MonoBehaviour
{
    public float waitTimeInSeconds = 1.5f;
    public int timeoutSeconds = 3;

    private double _startTime;

    protected IEnumerator Start()
    {
        _startTime = Time.realtimeSinceStartupAsDouble;

        Debug.Log($"Start: {gameObject.name}");

        yield return new WaitUntil(WaitTime, new TimeSpan(0, 0, 0, timeoutSeconds), LogTimeout);

        Debug.Log($"End: {gameObject.name}");
    }

    protected void LogTimeout()
    {
        Debug.Log($"Timeout: {gameObject.name}");
    }

    protected bool WaitTime()
    {
        while (Time.realtimeSinceStartupAsDouble - _startTime < waitTimeInSeconds)
        {
            return false;
        }

        return true;
    }
}

Seriously, why just say random things?

woven hare
granite pewter
#

it is a "built-in" overload

woven hare
#
public sealed class WaitUntil : CustomYieldInstruction
{
    private Func<bool> m_Predicate;

    public override bool keepWaiting => !m_Predicate();

    public WaitUntil(Func<bool> predicate)
    {
        m_Predicate = predicate;
    }
}
#

This is

#

It doesn't have an overload

granite pewter
#

I just showed you me using it, the c# documentation, and unity's c# reference. I'm not sure what else you want, but it exists in the WaitUntil class and you can use it.

woven hare
granite pewter
#

It must not be in every version. I'm not sure what version specifically it was added in, but things change between unity versions 🤷‍♂️

#

what's the error say?

woven hare
granite pewter
#

what's the error say though?

woven hare
#

Well, what can an error about overload say?

#

"It doesn't contain a constructor which takes 3 arguments"

granite pewter
#

then your unity version doesn't have the overload. It doesn't mean every unity version doesn't have that overload, so it's improper to say that it's not usable or that it's not a "built-in" overload.

woven hare
#

I'm using 2021 version, and so, of course, I assumed WaitUntil doesn't have an overload, as I have never seen it needing more than 1 argument, which is Func<bool>