Search Unity

Async WebRequest and await in the script where data is needed

Discussion in 'Scripting' started by EdiJung, Jul 21, 2022.

  1. EdiJung

    EdiJung

    Joined:
    Nov 10, 2020
    Posts:
    4
    My final Goal is to have a Start Screen where i start loading the data.
    If the User goes to the next screen, i have to check, if loading data already done (and show a loading indicator if its not done)

    At the moment for testing, i start the loading data in the Awake() function in the same Script.
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using System.Threading.Tasks;
    3. using UnityEngine;
    4.  
    5. public class PopulateShelf : MonoBehaviour
    6. {
    7.     private List<Task> tasks = new List<Task>();
    8.  
    9.     private void Awake()
    10.     {
    11.         tasks.Add(LoadingData.LoadData());
    12.     }
    13.     private void Start()
    14.     {
    15.         waitOnData().GetAwaiter().GetResult();
    16.         //Do Something with the data
    17.     }
    18.  
    19.     private async Task waitOnData()
    20.     {
    21.         await Task.WhenAll(tasks);
    22.     }
    23. }
    24.  
    According to this Video:
    with GetAwaiter().GetResult() i can await a async method from a non async method.
    And with await.Task.WhenAll(tasks) i try to wait until all data is loaded

    My WebRequest (LoadingData.LoadData()) looks like the following:
    Code (CSharp):
    1.  
    2. public class LoadingData : MonoBehaviour
    3. {
    4.     public static Dictionary<int, BookDTO> Books = new Dictionary<int, BookDTO>();
    5.     public static List<CategoryDTO> Categories = new List<CategoryDTO>();
    6.     public static async Task LoadData()
    7.     {
    8.         await LoadCategories();
    9.         LoadBooks();
    10.         await Task.Yield();
    11.     }
    12.     public static async Task LoadCategories()
    13.     {
    14.         UnityWebRequest request = UnityWebRequest.Get(Constants.apiURL + Constants.apiURLCategories);
    15.         await Task.Yield();
    16.         request.SendWebRequest();
    17.         if (request.result == UnityWebRequest.Result.ConnectionError)
    18.         {
    19.             Debug.Log(request.error);
    20.         }
    21.         else
    22.         {
    23.             Categories = JsonConvert.DeserializeObject<List<CategoryDTO>>(request.downloadHandler.text);
    24.         }
    25.  
    26.     }
    27.     public static async Task LoadBooks()
    28.     {
    29.         UnityWebRequest request = UnityWebRequest.Get(Constants.apiURL + Constants.apiURLBooks);
    30.         await Task.Yield();
    31.         request.SendWebRequest();
    32.         if (request.result == UnityWebRequest.Result.ConnectionError)
    33.         {
    34.             Debug.Log(request.error);
    35.         }
    36.         else
    37.         {
    38.             List<BookDTO> books = JsonConvert.DeserializeObject<List<BookDTO>>(request.downloadHandler.text);
    39.             books.ForEach(b => Books.Add(b.bookId, b));
    40.         }
    41.     }
    42. }
    43.  
    I have the feeling that it is quite messy... so for any improvements I am also very grateful
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,689
    Why not just use coroutines? Super-simple to reason about. Your web-get coroutine can accept a delegate that it calls back when the data is ready.
     
  3. EdiJung

    EdiJung

    Joined:
    Nov 10, 2020
    Posts:
    4
    Thanks for the answer.
    I first try it with coroutines but the video mention above says async/await are 'simpler'...
    But anyways, if i try it with coroutines.
    Then i would start the coroutine in the await.
    But how do i check if the coroutines is finished, when i need the data?
     
    Mentoster likes this.
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,689
    Go look at the example code for UnityWebRequest.
     
  5. nTu4Ka

    nTu4Ka

    Joined:
    Jan 27, 2014
    Posts:
    69
    It's probably late but async/await definitely easier to manage and more flexible. Tarodev has good examples why.
    I rewrote a portion of my code to get rid of coroutines and am really happy so far. :) async/await allowed me to decentralize code and get rid of unnecessary wrappers. Code got a lot cleaner and easier to read. :)

    There are some caveats though:
    -ThreadPool is not supported in WebGL as of now. So things like Task.Run() and ThreadPool commands will not work in web builds.
    -There are some deeper issues with async/await which you can find in top comment under Tarodev video.

    I can also propose to use UniTask instead of Task. It's a nice "wrapper" with great WebGL support, multiple features and clear documentation:
    https://github.com/Cysharp/UniTask#getting-started
     
    astropotato likes this.