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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Wait for a UnityWebRequest to complete

Discussion in 'Getting Started' started by geonolis, Sep 14, 2022.

  1. geonolis

    geonolis

    Joined:
    Jul 6, 2021
    Posts:
    5
    I am trying to fill in text objects with a c# script after a UnityWebRequest completes.

    I have tried to use StartCoroutine with an IEnumerator method, I have tried calling an async method using await... Everytime the web request is completed after the commands that are trying to read the results and add them to the UI text objects.

    Here is a part of my code:
    Code (CSharp):
    1.  
    2.    void Start()
    3.     {
    4.         this.TestGet();
    5.         this.DrawTable();
    6.     }
    7. public async void TestGet()
    8.     {
    9.         var url = "http://xxxxx";
    10.  
    11.         var httpClient = new HttpClient( my params );
    12.         this.myData = await httpClient.Get<Type>(url);
    13.     }
    14. public void DrawTable()
    15.     {
    16. // reading myData and adding text to UI
    17.     }
    18.  
    I have tried to insert a while loop checking that myData is not null but it became an endless loop.
    How can I make Unity wait before accessing myData ?

    Any ideas?
     
  2. Schneider21

    Schneider21

    Joined:
    Feb 6, 2014
    Posts:
    3,510
    Async/Await is fantastic and much better than getting stuck in Coroutine Hell, so stick with that approach! You're very close, I believe, and it might just be a small misunderstanding that's holding you up here.

    First let's talk about "async". Adding the async keyword to your method tells the compiler that the method can be run asynchronously. Once you've marked a method as async, it's allowed to have one of three return types: void, Task, and Task<TResult>. The only time you should use void is for event handlers or as entry points for async code. This is because while async methods with return type void can wait internally for async methods to complete, other methods can't wait on this method's result before continuing! You may have already spotted the issue by now, but we'll keep going.

    The other part is "await." Using await with a Task tells the compiler to continue executing at this location when the Task has been completed. While waiting for that Task to finish, your synchronous code is still running (that's everything outside the async method you're currently in), but everything after your await will only resume when the Task completes.

    So! Let's step through your code and see why it's doing what it's doing.
    Code (CSharp):
    1. void Start()
    2. {
    3.     this.TestGet();
    4.     this.DrawTable();
    5. }
    So your component's Start method gets called, and first thing we do is fire off TestGet. But wait! The compiler knows that TestGet is an async method (because you told it that it was) so it says "That may or may not take some time. Go ahead and start the next thing" and then it runs DrawTable.

    Eventually, TestGet finishes and says "Hey, I'm all done getting that test!" but at that point, nobody cares. There was no one waiting to see what TestGet got, and eventually the universe ceases to exist and no soul ever found out what happened with that method.

    So what do you do? Well first we have to change the return type of TestGet to Task. This way we can actually wait for the results of what that method does. Then we'll await the call to TestGet so that we're actually waiting for it to finish before proceeding. And finally, we have to indicate that Start itself is asynchronous so that we're even allowed to use await inside it.

    Code (CSharp):
    1. private async void Start()
    2. {
    3.     await TestGet();
    4.     DrawTable();
    5. }
    6.  
    7. private async Task TestGet()
    8. {
    9.     var url = "http://xxxxx";
    10.  
    11.     var httpClient = new HttpClient( my params );
    12.     this.myData = await httpClient.Get<Type>(url);
    13. }
    14.  
    15. private void DrawTable()
    16. {
    17.     // reading myData and adding text to UI
    18. }
    With this code in place (after, you know, actually putting your parts back in where they belong), you'll be able to wait for the results of a method before continuing with your synchronous code.

    Hopefully that helped you not just get past this problem, but actually understand a bit better how async/await works. It's a topic worth reading up on, as it's an incredibly powerful tool and absolutely essential when you're working with data from a network.

    Good luck!
     
    JoeStrout likes this.
  3. geonolis

    geonolis

    Joined:
    Jul 6, 2021
    Posts:
    5
    Understanding async / await, I managed to check the completion of WebRequest with something obvious: void Update(). Here it is:
    Code (CSharp):
    1. void Start()
    2.     {
    3.         justStarted = true;
    4.         this.TestGet();
    5.     }
    6.  
    7. void Update()
    8.     {
    9.         if (justStarted)
    10.         {
    11.             if (this.myData != null)
    12.             {
    13.                 this.DrawTable();
    14.                 justStarted = false;
    15.             }
    16.     }
    17.  
    18. public async void TestGet()
    19.      {
    20.         var url = "http://xxxxx";
    21.         var httpClient = new HttpClient( my params );
    22.         this.myData = await httpClient.Get<Type>(url);
    23.      }
    24.  
    25. public void DrawTable()
    26.     {
    27. // ....
    28.     }
    Thank you for your contribution!
     
    Last edited: Sep 14, 2022
  4. Schneider21

    Schneider21

    Joined:
    Feb 6, 2014
    Posts:
    3,510
    I mean... that's certainly a way to do it, too. May I ask what issue you had with the way I demonstrated it at the bottom of my post?

    The thing about doing it the way I showed is that it's... well, correct. It's using the tools (async/await) the way they're designed to be used.

    The downsides to doing it the way you showed here are:
    • You had to introduce an additional variable
    • You're now using Update (which runs 60 times every second) to do the same thing
    • The way you're doing it, if your http Get actually does return null for some reason, your DrawTable code will never execute, and you'll have no way of knowing that it didn't execute. With the way I showed, you could actually monitor the results of the request and handle them when they come back, executing DrawTable if needed or handling the error otherwise.
    It's totally up to you, of course, being your game and all. But I can promise you that learning how things are working and why will pay off so much more in the long run than just figuring out how to make a script "good enough."

    In any case, best of luck moving forward!
     
  5. geonolis

    geonolis

    Joined:
    Jul 6, 2021
    Posts:
    5
    Actually, I was trying to find a solution after posting the question and this was the code I had written before reading your answer...
    You are right about Update, so I implemented your solution and moved forward...
    Althought it works perfect at Unity Player, it fails as WebGL in any browser. It seems that WebGL does not wait for the async task to complete and loads my UI without any data...
    Are there any WebGL restrictions concerning await/async ?
    (The problem remains the same with any solution using await/async.)
    Thank you.
     
  6. Schneider21

    Schneider21

    Joined:
    Feb 6, 2014
    Posts:
    3,510
    Ah, it looks like there's been issues with multithreading support on WebGL. Seems like it was supposed to be fixed 2 years ago, but regression issues are kinda the norm with Unity these days. Reading that thread should give you a more complete picture of what you're dealing with, though, and may lead you to finding a workable solution for your needs.

    Good luck! Please report any progress here for the benefit of others running into the same issue who are too scared to post!