简体   繁体   中英

Waiting for a parse.com async task to finish in Unity3D

As part of my school project I'm trying to link two tables to decrease the amount of data stored in one table so I wanted to link my "Scores" class with my "CorrectAnswers" class via the ObjectID. However since the tasks are asynchronous, by the time one task is done saving, the other task has already begun or also finished saving and so the ObjectID returns as null.

Here's the code I'm using:

public void SaveScore()
{

    ParseObject SendScore = new ParseObject("Scores");
    SendScore["Score"] = CheckAnswer.score;
    SendScore["user"] = ParseObject.CreateWithoutData("_User", ParseUser.CurrentUser.ObjectId);
    SendScore["TestMode"] = MainMenu.testmode;
    SendScore["TotalQuestions"] = QuestionCreation.TotalQuestions;
    SendScore["CorrectQuestions"] = CheckAnswer.CorrectQuestions;


    SendScore.SaveAsync().ContinueWith(t =>
    {
        ScoreObjectId = SendScore.ObjectId;
    });

    ParseObject SendCorrectTopics = new ParseObject("CorrectAnswers");
    SendCorrectTopics["Score"] = SendScore.ObjectId;
    for (int i = 0; i <= 9; i++)
    {
        string Topic = "Topic" + (i + 1).ToString();
        SendCorrectTopics[Topic] = CheckAnswer.CorrectTopics[i];
    }

    SendCorrectTopics.SaveAsync();

    SceneManager.LoadScene(0);
}

How would I be able to make the second save hold until the first save has finished? I'm somewhat new to C# and so don't quite know all it's features yet. I've looked into "await" but unity doesn't seem to like that. Any help would be greatly appreciated! Thanks in advance,

EDIT: Okay, after a bit more reading on Unity's coroutines, I found a much better way of checking that only relies on checking when needed:

 IEnumerator CheckSave()
{
    while(ScoreObjectId == null & !DoneSave))
    {
        print("Running");
        yield return new WaitForSeconds(0.5f);
    }
    DoneSave = false;
    SaveTotalTopics();

}

This seems like a much better way of doing it.

Well, it seems the answer was something I've already done before, even if it is a little ugly.

Using Unity's update function I created a check to make sure the ObjectID is not null and the previous save had completed, as so:

void Update () {
    if (ScoreObjectId != null & DoneSave)
    {
        DoneSave = false;
        SaveTotalTopics();
    }

Thus splitting it into two saves and creating:

    public void SaveScore()
{
    ParseObject SendScore = new ParseObject("Scores");
    SendScore["Score"] = CheckAnswer.score;
    SendScore["user"] = ParseObject.CreateWithoutData("_User", ParseUser.CurrentUser.ObjectId);
    SendScore["TestMode"] = MainMenu.testmode;
    SendScore["TotalQuestions"] = QuestionCreation.TotalQuestions;
    SendScore["CorrectQuestions"] = CheckAnswer.CorrectQuestions;

    Task SendingScores = SendScore.SaveAsync().ContinueWith(t =>
    {
        if (t.IsFaulted || t.IsCanceled)
        {
            DoneSave = false;
            print(t.Exception);
        }
        else
        {
            DoneSave = true;
            print("Setting object ID!");
            ScoreObjectId = SendScore.ObjectId;
            print(ScoreObjectId); 
        }
    });

}



void SaveTotalTopics()
{
    for (int i = 0; i <= 9; i++)
    {
        string Topic = "Topic" + (i + 1).ToString();
        SendCorrectTopics[Topic] = CheckAnswer.CorrectTopics[i];
    }

    SendCorrectTopics["UserScore"] = ParseObject.CreateWithoutData("Scores", ScoreObjectId);
    SendCorrectTopics.SaveAsync().ContinueWith(t =>
    {
        if(t.IsFaulted || t.IsCanceled)
        {
            print(t.Exception);
        }
        else
        {
            print("Saved!");
        }
    });
}

I'd also forgotten to use ParseObject.CreateWithoutData() so my first code snippet wouldn't have worked even if I'd found a better method...

So, although I'm not happy with the final result, at least it works and I don't think running an if statement every frame should significantly impact on my game's performance.

SendScore.SaveAsync().ContinueWith(t =>
{
    ScoreObjectId = SendScore.ObjectId;
});

This is where you place actions you want to happen once the task is done. Try adding debug below the ScoreObjectId line to test it.

Why not use a bool and a while loop?

public IEnumerator SaveScore()
{

    bool canContinue = false;
    ParseObject SendScore = new ParseObject("Scores");
    SendScore["Score"] = CheckAnswer.score;
    SendScore["user"] = ParseObject.CreateWithoutData("_User", ParseUser.CurrentUser.ObjectId);
    SendScore["TestMode"] = MainMenu.testmode;
    SendScore["TotalQuestions"] = QuestionCreation.TotalQuestions;
    SendScore["CorrectQuestions"] = CheckAnswer.CorrectQuestions;


    SendScore.SaveAsync().ContinueWith(t =>
    {
        ScoreObjectId = SendScore.ObjectId;
        //set the bool canContinue to true because the first portion of code has finished running
        canContinue = true;
    });

    //wait while the canContinue bool is false
    while(!canContinue){
        yield return null;
    }

    //continue your parse code
    ParseObject SendCorrectTopics = new ParseObject("CorrectAnswers");
    SendCorrectTopics["Score"] = SendScore.ObjectId;
    for (int i = 0; i <= 9; i++)
    {
        string Topic = "Topic" + (i + 1).ToString();
        SendCorrectTopics[Topic] = CheckAnswer.CorrectTopics[i];
    }

    SendCorrectTopics.SaveAsync();

    SceneManager.LoadScene(0);
    return null;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM