Search Unity

  1. How can our website serve you better? Give us your feedback. Take our survey and let us know.
    Dismiss Notice

Await without Task

Discussion in 'Addressables' started by ProtoTerminator, Aug 14, 2020.

  1. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    477
    Just felt like making this extension to support awaiting handles directly. I figured some people could use this, especially with the Task property not working well in WebGL right now.

    Feel free to add this to the library, Addressables devs!

    P.S.
    handle.CompletedTypeless
    is misleading, I expected it to just let me add an
    Action
    without arguments, which would've made this extension more efficient.

    Code (CSharp):
    1. using System;
    2. using UnityEngine.ResourceManagement.AsyncOperations;
    3. using System.Runtime.CompilerServices;
    4.  
    5. public static class AsyncOperationHandleExtensions
    6. {
    7.     public struct AsyncOperationHandleAwaiter<T> : INotifyCompletion
    8.     {
    9.         private AsyncOperationHandle<T> _handle;
    10.  
    11.         public AsyncOperationHandleAwaiter(AsyncOperationHandle<T> handle)
    12.         {
    13.             _handle = handle;
    14.         }
    15.  
    16.         public bool IsCompleted
    17.         {
    18.             get
    19.             {
    20.                 return _handle.IsDone;
    21.             }
    22.         }
    23.  
    24.         public T GetResult()
    25.         {
    26.             if (_handle.Status == AsyncOperationStatus.Succeeded)
    27.             {
    28.                 return _handle.Result;
    29.             }
    30.             throw _handle.OperationException;
    31.         }
    32.  
    33.         public void OnCompleted(Action continuation)
    34.         {
    35.             _handle.Completed += _ => continuation();
    36.         }
    37.     }
    38.  
    39.     public struct AsyncOperationHandleAwaiter : INotifyCompletion
    40.     {
    41.         private AsyncOperationHandle _handle;
    42.  
    43.         public AsyncOperationHandleAwaiter(AsyncOperationHandle handle)
    44.         {
    45.             _handle = handle;
    46.         }
    47.  
    48.         public bool IsCompleted
    49.         {
    50.             get
    51.             {
    52.                 return _handle.IsDone;
    53.             }
    54.         }
    55.  
    56.         public object GetResult()
    57.         {
    58.             if (_handle.Status == AsyncOperationStatus.Succeeded)
    59.             {
    60.                 return _handle.Result;
    61.             }
    62.             throw _handle.OperationException;
    63.         }
    64.  
    65.         public void OnCompleted(Action continuation)
    66.         {
    67.             _handle.Completed += _ => continuation();
    68.         }
    69.     }
    70.  
    71.     /// <summary>
    72.     /// Used to support the await keyword for AsyncOperationHandle.
    73.     /// </summary>
    74.     public static AsyncOperationHandleAwaiter<T> GetAwaiter<T>(this AsyncOperationHandle<T> handle)
    75.     {
    76.         return new AsyncOperationHandleAwaiter<T>(handle);
    77.     }
    78.  
    79.     /// <summary>
    80.     /// Used to support the await keyword for AsyncOperationHandle.
    81.     /// </summary>
    82.     public static AsyncOperationHandleAwaiter GetAwaiter(this AsyncOperationHandle handle)
    83.     {
    84.         return new AsyncOperationHandleAwaiter(handle);
    85.     }
    86. }
     
    xeniaosense and LucasHehir like this.
  2. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    477
    Then that leads to these simple extensions which can be used to get a Task that will actually work properly in WebGL:


    Code (CSharp):
    1. public static async Task<T> ToTask<T>(this AsyncOperationHandle<T> handle)
    2. {
    3.     return await handle;
    4. }
    5.  
    6. public static async Task<object> ToTask(this AsyncOperationHandle handle)
    7. {
    8.     return await handle;
    9. }
     
    _watcher_ and LucasHehir like this.
  3. realcosmik

    realcosmik

    Joined:
    Nov 27, 2018
    Posts:
    20
    what makes this work over the regular asynoperationhandle handle in webgl? isnt this just a wrapper?
     
  4. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    477
    These are extensions that let you use the
    await
    keyword directly on an asynoperationhandle. Without this, you cannot
    await
    the handle without accessing the
    Task
    property. But the
    Task
    property doesn't work in WebGL, so the
    ToTask
    extensions here let you create a Task that does work in WebGL (so you can use it in
    Task.WhenAll()
    , for example).
     
  5. nir_unity1

    nir_unity1

    Joined:
    Nov 26, 2020
    Posts:
    12
    I gain errors when implementing this script

    Assets\AsyncOperationHandleExtensions.cs(87,33): error CS1983: The return type of an async method must be void, Task or Task<T>

    Assets\AsyncOperationHandleExtensions.cs(87,25): error CS0308: The non-generic type 'Task' cannot be used with type arguments

    for each of the functions
    both functions are places in the AsyncOperationHandleExtensions folder, Task is still not recognized.
    which libraries are in the "using" to solve it?
     
  6. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    477
    I'm not sure what your issue is. Are you using the .Net 4.x scripting runtime? async/await do not work in the .Net 3.5 scripting runtime.
     
  7. ugurberkecanunlu

    ugurberkecanunlu

    Joined:
    Dec 20, 2019
    Posts:
    6
    sorry but i cant undestand how can i use this script on my project ?
     
  8. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    477
    Something like this. Note that async/await requires .Net 4.x scripting runtime.

    Code (CSharp):
    1. async void LoadAndInstantiate(string address)
    2. {
    3.     var handle = Addressables.LoadAssetAsync<GameObject>(address);
    4.     var go = await handle;
    5.     Instantiate(go);
    6. }
     
  9. ugurberkecanunlu

    ugurberkecanunlu

    Joined:
    Dec 20, 2019
    Posts:
    6
    i gave an error " : 'AsyncOperationHandle<GameObject>' does not contain a definition for 'GetAwaiter' and no accessible extension method 'GetAwaiter' accepting a first argument of type 'AsyncOperationHandle<GameObject>' could be found (are you missing a using directive or an assembly reference?) " how can i fix ?


    also what are the difference between reference.InstantiateAsync() and Instantiate ?
    can I load an object before instantiating? without instantiateAsync?
     
    Last edited: Jan 24, 2021
  10. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    477
    You need to include the script from my original post in your project. ^^^

    InstantiateAsync is an addressables function that keeps track of the internal reference counter so that the memory can be released when the object is destroyed (only works with Addressables.ReleaseInstance). My example there loads the asset without instantiating it, then instantiates it with the synchronous Object.Instantiate, which addressables does not track.
     
  11. ugurberkecanunlu

    ugurberkecanunlu

    Joined:
    Dec 20, 2019
    Posts:
    6
    yeah i got it but i dont understand where should i add main script ? which folder ?
     
  12. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    477
    It doesn't matter, you can place it anywhere under Assets. I usually place all my scripts under Asset/Scripts. You might even place it in something like Assets/Scripts/Extensions.
     
unityunity