Search Unity

Method always returning an empty

Discussion in 'Scripting' started by EOMedvis, Jun 19, 2019.

  1. EOMedvis

    EOMedvis

    Joined:
    Feb 19, 2019
    Posts:
    94
    I'm having trouble understanding why my script is always returning an empty. I'm trying to make a script that grabs a Json from an online database (Google Firebase) and returning it as a dictionary.

    The Code are as follows:

    Code (CSharp):
    1.  
    2. public Dictionary<string, LeaderboardUser> GetData() //returns a list of the users and all of their scores as a dictionary.
    3.     {
    4.         Dictionary<string, LeaderboardUser> users = new Dictionary<string, LeaderboardUser>();
    5.  
    6.         RestClient.Get(databaseURL + ".json").Then(response =>
    7.         {
    8.             Debug.Log(response.Text);
    9.             users = JsonConvert.DeserializeObject<Dictionary<string, LeaderboardUser>>(response.Text);          
    10.             Debug.Log("# of users in users: " + users.Count);
    11.         }
    12.         ).Catch(error =>
    13.         {
    14.          Debug.Log(error); //dumps errors into console if something goes wrong during the data retrieval and processing.      
    15.         });
    16.         Debug.Log("# of users in users: " + users.Count);
    17.         return users;
    18.     }
    19.  
    I'm using the RestClient unity asset for the database communication, and JSON.NET for the deseralization. I've checked with the Debug.Log method to make sure the JSON is received as text, and that it's being deserliazed with the second Debug.Log (it shows the number of users as 2, which is the correct value matching the database). However, the third debug always shows 0. And the method always returns an empty dictionary with a count of 0.

    I'm unsure where I'm going wrong in this. The RestClient documentation doesn't offer any solutions. Nor do they have a forum.

    Any help would be much appreciated.
     
  2. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    I haven't used RestClient, but it looks like RestClient.Get() is probably returning a promise, and Then() is specifying a function that should happen when it finishes.

    If correct, that means the order of steps in your code is:

    1. Create an empty dictionary.
    2. Tell RestClient to start downloading some data.
    3. Return the dictionary.
    4. An indefinite time later, when RestClient is finished with its download, you run the code inside of Then() to try and parse the data.

    In short, you are trying to use an asynchronous function as if it were synchronous. You can't get the result of a download immediately. Instead of trying to return the result right away, you need to use some kind of event or callback to notify you when the results are available.
     
  3. EOMedvis

    EOMedvis

    Joined:
    Feb 19, 2019
    Posts:
    94
    Ah, thank you. that explains why I'm getting a 0 before the 2 comes back.

    Is there a way to tell the function to wait? I'm currently looking up Tasks. Is that the right direction to go?
     
  4. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    If you are running on the main Unity thread (which is the normal case for Unity development), then you can make the function wait, but you shouldn't, because your entire game will lock up and become nonresponsive while you are waiting.

    I haven't used Unity Tasks. (They might or might not be appropriate here; I simply don't know.)

    One option would be to use a Unity coroutine and have the coroutine wait (using "yield return") until the task finishes. If you were using UnityWebRequest instead of RestClient, you could even yield the request directly and that automatically means "wait until this is done". However, coroutines can't return a value in the normal sense, so that doesn't work with your current program structure.

    A more general way of handling things of this general nature is to make the deferred code (the part inside of "Then") responsible for notifying the rest of your code when it's done. For instance:
    1. You can make it set a flag somewhere to indicate that it's done, and you can have an Update() function somewhere that checks the value of that flag each frame.
    2. You can make it directly call some other function (probably you'd pass in a delegate as an argument, so the caller can specify which code should be called when it's done)
    3. You can make your function also return a promise (in which case whoever calls you has to deal with this problem)
    However, one thing to keep in mind is that most of the Unity engine is only safe to access from the main Unity thread. If you're on a separate thread, you can do whatever you want with your internal data, but if you want to get input from the user or update the UI or anything like that, you'll probably have to do it from the main thread.

    Most of the time in Unity, you are on the main thread anyway, so this is fine. But if you use a non-Unity asynchronous library, then it's not necessarily guaranteed that your asynchronous code will be on the main Unity thread (I'd have to do some research to know in this case). In this case, option #1 is "safe" because the thing checking for the flag is on the main thread, so it can do whatever it wants, and the asynchronous code merely sets the flag. Option #2 might not be safe.
     
  5. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,748
    Unity tasks are not designed to run long-time jobs. It's main purpose is to split single game frame to multiple tasks and run them on multi core cpu. The recommended way to wait for web request to complete is coroutine.
     
  6. EOMedvis

    EOMedvis

    Joined:
    Feb 19, 2019
    Posts:
    94
    Thank you both, I will look up the solutions you've provided in more detail. :)