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. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

How can I check if async function is finished?

Discussion in 'Scripting' started by mercem, Aug 1, 2018.

  1. mercem

    mercem

    Joined:
    Jun 9, 2018
    Posts:
    4
    Hi,

    I have read other posts about the same problem but they are not exactly same with my problem. I have created the bool flag to control if the asynchronous function is finished or not. Below snippet is what I am doing right now but the problem is because of while loop Unity freezes as if it never escape while loop but it should break while loop since waitFlag changes in the "getAllElementsAsync" method. Without while loop "getAllElements" method returns the value as null since it returns value before it is changed by "getAllElementsAsync" method. I am open to any other solutions that can solve my problem which is returning value after async method finishes. I have also tried creating coroutine for getAllElementsAsync method but it still returns value before coroutine finishes.



    Thank You

    Code (CSharp):
    1. public class DatabaseConnection : MonoBehaviour {
    2.  
    3. private DataSnapshot dataSnapshot;
    4. private bool waitFlag = true;
    5.  
    6.     public Datasnapshot getAllElements(){
    7.     getAllElementsAsync();
    8.  
    9.     while(this.waitFlag){}
    10.     return this.dataSnapshot
    11.     }
    12.  
    13.     public void getAllElementsAsync(){
    14.         FirebaseApp.DefaultInstance.SetEditorDatabaseUrl("someURL");
    15.         DatabaseReference reference = FirebaseDatabase.DefaultInstance.RootReference;
    16.        reference.GetValueAsync().ContinueWith(task => {
    17.             if (task.IsFaulted)
    18.             {
    19.                 Debug.Log("not ok");
    20.             }
    21.             else if (task.IsCompleted)
    22.             {
    23.                Debug.Log("ok");
    24.                 this.dataSnapshot = task.Result;
    25.                 this.waitFlag = false;
    26.          }
    27.         });
    28.     }
    29. }
     
  2. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    Your while loop is in
    getAllElements
    . So that is the method that needs to be a Coroutine. Something else will then need to start that method as a Coroutine and the while loop will need to contain a
    yield return null;
    statement. Give that a try.
     
  3. mercem

    mercem

    Joined:
    Jun 9, 2018
    Posts:
    4
    But I need to return a value from that function so as far as I know I cannot return value from coroutine.
     
  4. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,731
    You can use a callback (system.Action).

    If you need an example I can knock one up a bit later for you.
     
  5. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,731
    Here's a quick example, which should give you an idea of how to return a value from a coroutine, which you can then adapt to suit your needs.

    Drag this script onto any game object to test it.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class CoroutineExample : MonoBehaviour {
    6.  
    7.     // Use this for initialization
    8.     void Start () {
    9.         Debug.Log("Starting Coroutine...");
    10.         StartCoroutine(CalculateNumber(value => {
    11.             Debug.Log("Coroutine returned value is: " + value);
    12.         }));
    13.     }
    14.  
    15.     IEnumerator CalculateNumber(System.Action<int> callback)
    16.     {
    17.         int returnValue = 0;
    18.         for(int n = 0; n < 100; n++)
    19.         {
    20.             returnValue += Random.Range(10, 200);
    21.             yield return null;
    22.         }
    23.  
    24.         callback(returnValue);
    25.     }
    26. }
    27.  
     
  6. mercem

    mercem

    Joined:
    Jun 9, 2018
    Posts:
    4
    I just did the same thing with this I have encountered two problem the first one is that I can get "value" in console but I cannot get it's value in the Start() method. I mean I cannot get value like this:
    Code (CSharp):
    1.  var value = StartCoroutine(CalculateNumber(value => {
    2.            return value;
    3.         }));
    because System.Action does not return value and System.Func doesnt take parameter so I couldn't use any of these.
    Another problem is that I need to return value because I call this function from several other scripts.
     
  7. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    There are a number of things wrong with this
    var value = StartCoroutine(CalculateNumber(value => { return value; }));

    1. The
      var value
      resolves to a type of "Coroutine" because that is what the
      StartCoroutine
      returns.
    2. return value;
      is being called in the context of the called function. The call stack of the original calling code has long gone and is not relevant to the execution time of this line of code.
    3. Having different variables with the same name is confusing and does not force them to be the same thing / type / value.
    You need to fundamentally understand what is going on with asynchronous methods. You can't force a return value from an asynchronous method to other synchronous callers (without blocking as you have already discovered).

    So everything that needs to get this return value will need to supply their own callbacks. When the asynchronous method is finished, it will need to go through all the call back functions to allow all the awaiting objects to react.

    Another way to handle this is using events (either raw C# events or Unity events (this link is with one argument)).
     
  8. mercem

    mercem

    Joined:
    Jun 9, 2018
    Posts:
    4
    I gave this as an example I know what it returns but in order you to understand what I am trying to get i wrote that.
    Code (CSharp):
    1. var value = StartCoroutine(CalculateNumber(value => { return value; }));
    I am not very familiar with c# but I will check your suggestions. I want to ask if you can suggest me structure in order to get value from async function. For example how to use events in this code?
     
  9. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    First of all, is there any specific reason why you use a MonoBehaviour for the class in your first post? It could as well be a normal class.

    Secondly, it's usually the best if you use async-await all they way up until there is actually a caller who needs to proceed after the asynchronous stuff has been fired.

    That is, I'd recommend to turn your getAllElementsAsync into an awaitable method. If that's not an option and you want to be compatible to older C# versions - either use an event, a callback or a return type that keeps track of the async information.
    The event would reside in the declaring type that the async method is called on, and you should dispatch it properly.
    The callback would be an additional delegate parameter, that callback should be dispatched as well.
    Third option is to output an object which can be polled or subscribed to, either it is returned by the async method or available via an ref/out parameter.