Search Unity

Http Requests Without Coroutines

Discussion in 'Scripting' started by MrDos1, Sep 14, 2017.

  1. MrDos1

    MrDos1

    Joined:
    Dec 14, 2016
    Posts:
    20
    Hi all,

    I need to write a script in which some POST requests are made.

    At the moment, I'm using Unity coroutines with UnityWebRequest to send everything.
    This works fine but I have one more constraint which makes using coroutine not quite right.

    I cannot have a GameObject to run the coroutines on.
    Ultimately, the output of what I'm doing is gonna to be a Unity Package.
    That means if I rely on game objects, it is easy for the implementer to mess with my coroutines, change the name of my objects, or stop my requests, even not intentionally!
    The good thing is that I don't need to go back to the main thread after sending my requests.

    The best exemple of what I want is Unity's builtin analytics.
    These are sent at runtime, even on app quit, but still without any GameObject and DontDestroyOnLoad running coroutines.

    I tried creating a DLL using System.Net.Http but as soon as I include it in my Unity project, it's missing the imports... Manually adding System.Net.Http.dll did not fix the issue.

    Anyone would be able to help me?

    Thank you very much
     
  2. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    You don't need to use a coroutine. You can directly iterate it yourself using IEnumerator.MoveNext().
     
  3. MrDos1

    MrDos1

    Joined:
    Dec 14, 2016
    Posts:
    20
    That's indeed not a bad idea! Will try that out.
     
  4. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    To clarify... you don't need to use Unity to create and run a coroutine when you can handle it yourself.

    Example:

    Code (csharp):
    1.  
    2.     public class Foo
    3.    {
    4.        IEnumerator _iterator;
    5.  
    6.        public Foo()
    7.        {
    8.        }
    9.  
    10.        public void Update()
    11.        {
    12.            if (_iterator == null)
    13.            {
    14.                Debug.Log("Starting DoSomething()");
    15.                _iterator = DoSomething();
    16.            }
    17.  
    18.            if (!_iterator.MoveNext())
    19.            {
    20.                Debug.Log("DoSomething completed!");
    21.                _iterator = null;
    22.            }
    23.        }
    24.  
    25.        IEnumerator DoSomething()
    26.        {
    27.            int i = 0;
    28.  
    29.            while (i < 10)
    30.            {
    31.                ++i;
    32.                yield return null;
    33.                Debug.Log("yielding...");
    34.            }
    35.        }
    36.    }
    37.  
    That should give you an idea of how to handle it yourself.
     
    Last edited: Sep 14, 2017
  5. MrDos1

    MrDos1

    Joined:
    Dec 14, 2016
    Posts:
    20
    That might do the trick! I might be able to run a while loop in an async method!
    Thanks mate!
     
  6. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    No problem. Good luck!

    Note: I edited the code above because I forgot to put in "++i"... kind of an important piece of code to leave out ;)
     
  7. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,188
    This may also offer a solution https://www.assetstore.unity3d.com/en/#!/content/54975

    More effective coroutines don't stop if the gameobject is destroyed, so there may be other options for using it, though I've only touched on it once to test speed and not much else.

    Just a thought, no guarantee they will fit your use case without testing.
     
  8. MrDos1

    MrDos1

    Joined:
    Dec 14, 2016
    Posts:
    20
  9. MrDos1

    MrDos1

    Joined:
    Dec 14, 2016
    Posts:
    20
    Too bad... The package is also using a GameObject as a singleton to run all its coroutines.

    Almost working! Problem is that UnityWebRequest prevents iterations with MoveNext when called from a background thread...
    I should've see that coming.
     
  10. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    Oh. Heh. Ugh.

    I suppose it makes sense since you're required to use the main thread for any access to the Unity API.

    I actually don't use UnityWebRequest, but use the .NET HttpWebRequest instead. Are you required to use UnityWebRequest?
     
  11. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,735
    In future version of Unity we will ship an update to UnityWebRequest where you will be able to connect a handler to completion event. Right now if you want to avoid coroutines/game objects and fire UnityWebRequest with ability to track it's progress, I think the simplest option you have is to attach a custom DonwloadHandler script, it will be invoked for downloaded data. There is an issue with it though: you won't get any invocations for failed requests.
     
    _TheFuture_ likes this.
  12. MrDos1

    MrDos1

    Joined:
    Dec 14, 2016
    Posts:
    20
    That is nice! Thanks for the info.
    I'm looking into @BlackPete 's idea to use System.Net.HttpWebRequest. Combining that with a Task.Run call to avoid blocking the main thread.
    It seems to work perfectly at the moment. Is there any restriction I am not aware of, that could explain why this solution isn't the one you recommend?
     
  13. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,735
    HttpWebRequest may not be available on all platforms and it might be less efficient than UnityWebRequest on some platforms (i.e. consume more battery). If that does not concern you, feel free to use it.
     
  14. MrDos1

    MrDos1

    Joined:
    Dec 14, 2016
    Posts:
    20
    I understand! Thank you very much, I'll run some tests to see if I should be concerned.
     
  15. pshtif

    pshtif

    Joined:
    Mar 6, 2017
    Posts:
    12
    When is the update, to be able to connect handler on completion event, going to be done? I mean it is pretty old topic and yet there doesn't seem to be a simple way to use UnityWebRequests without using MonoBehavior and coroutines.
     
    _TheFuture_ and hansi_reit like this.
  16. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,735
    Can't you do that? It's shipped for quite a while already.
     
  17. pshtif

    pshtif

    Joined:
    Mar 6, 2017
    Posts:
    12
    Aurimas sorry I am replying so late as I found my way around it. But can you provide a snippet how to use the request without coroutines?
     
  18. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,735
  19. pshtif

    pshtif

    Joined:
    Mar 6, 2017
    Posts:
    12
    Thanks Aurimas, I will look into that.
     
  20. m_blazin

    m_blazin

    Joined:
    Jul 25, 2018
    Posts:
    2
    Do you have a working example for this completed handler? I am trying to use it but am getting an Exception saying the download handler is already closed. This is my example code snippet:

    Code (CSharp):
    1.  
    2. using (UnityWebRequest req = UnityWebRequest.Get(String.Format("{0}/players/{1}/offers", endpoint, userId)))
    3.             {
    4.                 UnityWebRequestAsyncOperation asyncOperation = req.SendWebRequest();
    5.                 asyncOperation.completed += (asyncOperation) =>
    6.                 {
    7.                     byte[] result = req.downloadHandler.data;
    8.                     // throws NullReferenceException: DownloadHandler has already been destroyed
    9.                 };
    10.                 yield return null;
    11.  
    12.             }
    If I use a co-routine and just yield return the UnityWebRequestAsyncOperation I don't get this error, but I'm also writing a SDK and don't want to use co-routines/game objects.
     
    vamidi16 likes this.
  21. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,735
    Your bug is that you put your request inside using block, which means UWR and it's handlers get disposed when you exit this block. With completion event don't put UWR in using block, instead call Dispose() on it when you are done (in completion event handler of course).
    BTW, you can get UnityWebRequest from async operation.
     
    Kurt-Dekker likes this.
  22. m_blazin

    m_blazin

    Joined:
    Jul 25, 2018
    Posts:
    2
    Yes that was it, thanks a lot for your explanation!
     
  23. hyphenbash

    hyphenbash

    Joined:
    Dec 31, 2018
    Posts:
    20
    @Aurimas-Cernius can you please confirm which part of the UWR is threaded and which parts use coroutines or async/await internally?
     
  24. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,735
    SendWebRequest() kicks the request on a different thread. From there all runs on different thread until completion with some special case related to particular download handlers:
    - DownloadHandlerAudioClip only caches data in memory, AudioClip currently is create on main thread when you access it
    - DownloadHandlerTexture does part of texture creation on other thread, but requires some finalizing work to be done on main thread
    - DownloadHandlerAssetBundle does decompression and caching on other thread, but final step is done on main thread uppon accessing the bundle
    - DownloadHandlerScript caches the incomming data, the script itself in invoked once per frame on main thread (if some data arrived during that frame).
     
  25. hyphenbash

    hyphenbash

    Joined:
    Dec 31, 2018
    Posts:
    20
    so tcp connection, read/write data streams are all done on another thread using sync/async operations?
     
    Last edited: Sep 4, 2019
  26. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,735
    Yes. WebGL is different since it has no threads, but at least it's async there too.
     
  27. hyphenbash

    hyphenbash

    Joined:
    Dec 31, 2018
    Posts:
    20
    Great. Thanks for the information!
     
  28. DavidSWu

    DavidSWu

    Joined:
    Jun 20, 2016
    Posts:
    183
    In the future, could we have a callback that gets executed in a different thread on request completion? For example, say we want to parse some JSON from the request. Right now Unity jumps to the main thread and we queue a task to get the parse to run on a background thread, then queue an action when we are done parsing and need to access something on the main thread.

    Not a big deal, but saving a jump back and forth would be nice.
     
    AviarLabs and hyphenbash like this.