Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question ExecuteLater works only once (bug?)

Discussion in 'UI Toolkit' started by Maverick, Aug 10, 2023.

  1. Maverick

    Maverick

    Joined:
    Dec 18, 2009
    Posts:
    224
    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.

    Code (CSharp):
    1. public class Test : MonoBehaviour
    2.     {
    3.         public UIDocument uiDocument;
    4.  
    5.         private IVisualElementScheduledItem _shi;
    6.  
    7.         void Start()
    8.         {
    9.             var ve = new VisualElement();
    10.             uiDocument.rootVisualElement.Add(ve);
    11.  
    12.             _shi = ve.schedule.Execute(Repeat).StartingIn(1000);
    13.         }
    14.  
    15.         private void Repeat(TimerState obj)
    16.         {
    17.             UnityEngine.Debug.Log($"Repeat {obj.deltaTime}");
    18.             _shi.ExecuteLater(1000);
    19.         }
    20.     }
     
  2. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,111
  3. Maverick

    Maverick

    Joined:
    Dec 18, 2009
    Posts:
    224
    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.
     
  4. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,111
    Ah I see. Well here is the source of ExecuteLater():
    Code (CSharp):
    1. public void ExecuteLater(long delayMs)
    2. {
    3.     if (!isScheduled)
    4.     {
    5.         Resume();
    6.     }
    7.     ResetStartTime();
    8.     StartingIn(delayMs);
    9. }
    Source: https://github.com/Unity-Technologi....unity.ui/Core/VisualElementScheduler.cs#L258

    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:
    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using UnityEngine;
    4. using UnityEngine.UIElements;
    5.  
    6. public class Test : MonoBehaviour
    7. {
    8.     public UIDocument uiDocument;
    9.  
    10.     private IVisualElementScheduledItem _shi;
    11.  
    12.     void Start()
    13.     {
    14.         var ve = new VisualElement();
    15.         uiDocument.rootVisualElement.Add(ve);
    16.  
    17.         _shi = ve.schedule.Execute(Repeat).StartingIn(1000);
    18.     }
    19.  
    20.     private void Repeat(TimerState obj)
    21.     {
    22.         StartCoroutine(RepeatCoroutine(obj));
    23.     }
    24.  
    25.     private IEnumerator RepeatCoroutine(TimerState obj)
    26.     {
    27.         yield return null;
    28.  
    29.         UnityEngine.Debug.Log($"Repeat {obj.deltaTime}");
    30.         _shi.ExecuteLater(1000);
    31.     }
    32. }
    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 :)
     
    Last edited: Aug 11, 2023
  5. Maverick

    Maverick

    Joined:
    Dec 18, 2009
    Posts:
    224
    Yep, ended up with similar solution

    Code (CSharp):
    1. private void Repeat(TimerState obj)
    2. {
    3.       UnityEngine.Debug.Log($"Repeat {obj.deltaTime}");
    4.       _ve.schedule.Execute(() => _shi.ExecuteLater(1000));          
    5. }
    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. :)

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

    Thanks
     
    _geo__ likes this.
  6. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,111
    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.
     
    Maverick likes this.
  7. Maverick

    Maverick

    Joined:
    Dec 18, 2009
    Posts:
    224
    Bump (for Unity)