ExecuteLater works only once (bug?)

Hello,

Following code executes Repeat method only once. I would expect that method is called (about) each second repeatedly.

Is it an expected ExecuteLater behaviour or a bug?

Thanks.

public class Test : MonoBehaviour
    {
        public UIDocument uiDocument;

        private IVisualElementScheduledItem _shi;

        void Start()
        {
            var ve = new VisualElement();
            uiDocument.rootVisualElement.Add(ve);

            _shi = ve.schedule.Execute(Repeat).StartingIn(1000);
        }

        private void Repeat(TimerState obj)
        {
            UnityEngine.Debug.Log($"Repeat {obj.deltaTime}");
            _shi.ExecuteLater(1000);
        }
    }

You are probably looking for something like this:

// call every 1000 MS
_shi = ve.schedule.Execute(Repeat).Every(1000);

// Call every 1000 MS with a starting delay of 5000 MS
// _shi = ve.schedule.Execute(Repeat).StartingIn(5000).Every(1000);

Thanks for reply. I know about this method, but I have a certain use case when I need ExecuteLater.
Of course there are other ways to implement it, I just faced the problem and would like to know if it’s a bug or normal behaviour.

Ah I see. Well here is the source of ExecuteLater():

public void ExecuteLater(long delayMs)
{
    if (!isScheduled)
    {
        Resume();
    }
    ResetStartTime();
    StartingIn(delayMs);
}

Source: UnityCsReference/ModuleOverrides/com.unity.ui/Core/VisualElementScheduler.cs at 2022.1 · Unity-Technologies/UnityCsReference · GitHub

Based on the docs I think our expectation is correct but my guess would be that it is unscheduled AFTER the exection and thus you can’t reschedule it immediately (within the executed function itself).

To test this theory I did this and it worked:

using System.Collections;
using UnityEngine;
using UnityEngine.UIElements;

public class Test : MonoBehaviour
{
    public UIDocument uiDocument;

    private IVisualElementScheduledItem _shi;
 
    void Start()
    {
        var ve = new VisualElement();
        uiDocument.rootVisualElement.Add(ve);

        _shi = ve.schedule.Execute(Repeat).StartingIn(1000);
    }

    private void Repeat(TimerState obj)
    {
        StartCoroutine(RepeatCoroutine(obj));
    }

    private IEnumerator RepeatCoroutine(TimerState obj)
    {
        yield return null;

        UnityEngine.Debug.Log($"Repeat {obj.deltaTime}");
        _shi.ExecuteLater(1000);
    }
}

Hope this helps. Though I would not consider this a proper solution. More like an experiment.

I am still not 100% sure what you want to achieve. Can you not just use ExecuteLater() first and then in there schedule a new function with Every()? What I do unterstand is the urge to reuse an already created scheduler instead of creating a new one :slight_smile:

Yep, ended up with similar solution

private void Repeat(TimerState obj)
{
      UnityEngine.Debug.Log($"Repeat {obj.deltaTime}");
      _ve.schedule.Execute(() => _shi.ExecuteLater(1000));           
}

As for use case: sometimes you need to check if a certain process has finished and you need to do some work after that. Using ExecuteLater() seems convenient. Of course I ca do it via Every() and other means. :slight_smile:

Still I’m eager to hear from unity what is expected behaviour.

Thanks

1 Like

Me 2. I think allowing rescheduling within the executed method would be more in line with how the docs describe ExecuteLater() → “Cancels any previously scheduled execution of this item and re-schedules the item.”

Can’t you do that with this (pseudo code): .Every(updateProgressBar).Until(processIsDone) where “processIsDone()” will return a bool but also do the thing you want it to do once the process is done.

1 Like

Bump (for Unity)