Retrieve Data from Realtime Database with Action Callback

痴心易碎 提交于 2021-02-05 09:26:27

问题


I am trying to receive the JSON value from the Realtime Database of Firebase using Unity.

I do the following:

   FirebaseDatabase.DefaultInstance
          .GetReference("Leaders").OrderByChild("score").GetValueAsync().ContinueWith(task =>
                    {
                        if (task.IsFaulted)
                        {
                            Debug.LogError("error in reading LeaderBoard");
                            return;
                        }
                        else if (task.IsCompleted)
                        {
                            Debug.Log("Received values for Leaders.");
                            string JsonLeaderBaord = task.Result.GetRawJsonValue();
                            callback(JsonLeaderBaord);
                        }
        }
  });

Trying to Read the CallBack :

private string GetStoredHighScores()
    {
      private string JsonLeaderBoardResult;
      DataBaseModel.Instance.RetriveLeaderBoard(result =>
            {
                JsonLeaderBoardResult = result; //gets the data

            });
  return JsonLeaderBoardResult; //returns Null since it doesn't wait for the result to come.
}

Question is how do i wait for the callback to return the value and afterwards return the value of the JsonLeaderBoardResult.


回答1:


return JsonLeaderBoardResult; //returns Null since it doesn't wait for the result to come.

The RetriveLeaderBoard function doesn't return immediately. You can either use coroutine to wait for it or return the JsonLeaderBoardResult result via Action. Using Action make more sense in your case.

Change the string return type to void then return the result through Action:

private void GetStoredHighScores(Action<string> callback)
{
    string JsonLeaderBoardResult;
    DataBaseModel.Instance.RetriveLeaderBoard(result =>
            {
                JsonLeaderBoardResult = result; //gets the data
                if (callback != null)
                    callback(JsonLeaderBoardResult);
            });
}

Usage:

GetStoredHighScores((result) =>
{
    Debug.Log(result);
});

EDIT:

That is great, but still need to do some stuff after getting the result in `GetStoredHighScores' outside the Action, otherwise i can get an error like: get_transform can only be called from the main thread.

You get this error because RetriveLeaderBoard is running from on another Thread. Grab UnityThread from this post then do the callback on the main Thread with UnityThread.executeInUpdate.

Your new code:

void Awake()
{
    UnityThread.initUnityThread();
}

private void GetStoredHighScores(Action<string> callback)
{
    string JsonLeaderBoardResult;
    DataBaseModel.Instance.RetriveLeaderBoard(result =>
            {
                JsonLeaderBoardResult = result; //gets the data

                UnityThread.executeInUpdate(() =>
                {
                    if (callback != null)
                        callback(JsonLeaderBoardResult);
                });
            });
}



回答2:


You're seeing a classical confusion with asynchronous APIs. Since loading data from Firebase may take some time, it happens asynchronously (on a separate thread). This allows your main code to continue, while the Firebase client is waiting for a response from the server. When the data is available, the Firebase client calls your callback method with that data, so that you can process it.

It's easiest to see what this does in your code by placing a few logging statements:

Debug.Log("Before starting to load data");
FirebaseDatabase.DefaultInstance
      .GetReference("Leaders").OrderByChild("score").GetValueAsync().ContinueWith(task => {
    Debug.Log("Got data");

    }
});
Debug.Log("After starting to load data");

When you run this code, it prints:

Before starting to load data

After starting to load data

Got data

This is probably not the order in which you expected the output. Due to the asynchronous nature of the call to Firebase, the second log line gets printed last. That explains precisely why you're seeing an empty array when you return it: by that time the data hasn't been loaded from Firebase yet, and your ContinueWith hasn't executed yet.

The solution to this is always the same: since you can't return data that hasn't loaded yet, you have to implement a callback function. The code you shared does that twice already: once for the ContinueWith of Firebase itself and one in RetriveLeaderBoard.

Any code that needs to have the up to date leaderboard, will essentially have to call RetriveLeaderBoard and do any work with the leaderboard data in its callback. For example, if you want to print the leaderboard:

DataBaseModel.Instance.RetriveLeaderBoard(result => {
    Debug.Log(result);
});


来源:https://stackoverflow.com/questions/53335689/retrieve-data-from-realtime-database-with-action-callback

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!