1

I have a problem with Unity WebGL and the way the downloaded data from a UnityWebRequests gets handled. In the standalone version I'm waiting for the downloaded data with while (!async.isDone) { } and then json serialize it. Afterwards I do the yield return null;. This way the data gets processed after the yield return when it got completely got handled first. But in WebGL the while (!async.isDone) { } is not allowed as in the official documents explained WebGL Networking.

So what is the best way to handle this problem? Something like calling the processing in the same coroutine after the handling finished? But then I still have the problem with creating gameObjects with the handled data since you can only create gameObjects in the main thread of unity itself.

Anyone got the same problem and found a solution?

Thanks in advance!

Update: Here is a complete code example:

private IEnumerator m_GetUserToken(string accesscode, string password)
{
    UnityWebRequest request = new UnityWebRequest(v_ServerIP + "api-token-auth/", "POST");

    byte[] bodyRaw = Encoding.UTF8.GetBytes("{\"username\":\"" + accesscode + "\", \"password\":\"" + password + "\"}");
    request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bodyRaw);
    request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
    request.SetRequestHeader("Content-Type", "application/json");
    request.chunkedTransfer = false;
    UnityWebRequestAsyncOperation async = request.SendWebRequest();

    while (!async.isDone) { }

    if (request.isNetworkError || request.isHttpError)
    {
        v_TokenSuccess = false;
        Debug.Log("Token Error: " + request.error);
        Debug.Log("Token Response Code: " + request.responseCode);
    }
    else
    {
        v_UserToken = JsonUtility.FromJson<UserToken>(request.downloadHandler.text);
        v_TokenSuccess = true;
    }
    yield return Timing.WaitForOneFrame;
}

Update2:

Since it is still somehow unclear here is another example:

#Main Thread

string s = "";

StartCoroutine(functionname);
Debug.Log(s);

#Coroutine
private IEnumerator functionname(){
   downloadTextfromUrl
   while(!downloadFinished) {yield return null}
   s = downloadText;
}

So after yield return null is called the Debug.Log(s) gets called and s is still empty since s = downloadText hasn't been called yet.

1
  • In other words: You need to find a way to wait in the main thread until a co-routine has finished? Commented Sep 26, 2018 at 12:01

1 Answer 1

4

While waiting for the UnityWebRequest request to finish with the async.isDone property, you have to yield inside that while loop. Your current code can cause an infinite loop on Android devices or make the download fail. The documentation you linked, says that you can use while (!async.isDone) but you have to yield it. This applies to every platform not just WebGL.

Replace

while (!async.isDone) { }

with

while (!async.isDone) { yield return null; }

This will make the while loop execute every frame and check if async.isDone is true instead of blocking the whole program.


Note:

With your second edit, it looks like you also want to access the downloaded data outside the function. For example, you want to do this:

StartCoroutine(functionname());
//then use the downloaded data
Debug.Log(downloaded_data);

You have two ways to do this:

1. Yield the coorutine function like I did above but with yield return StartCoroutine(functionname()). This must be done from another coroutine function:

The request function:

string v_UserToken;

IEnumerator m_GetUserToken(string url)
{
    UnityWebRequest request = UnityWebRequest.Get(url);
    UnityWebRequestAsyncOperation async = request.SendWebRequest();
    while (!async.isDone) { yield return null; }

    if (request.isNetworkError || request.isHttpError)
    {
        v_UserToken = request.error;
    }
    else
    {
        v_UserToken = request.downloadHandler.text;
    }
}

Then wait for it to finish after calling it:

IEnumerator AnotherCoroutineFunc()
{
    //Call and wait for the m_GetUserToken coroutine to finish
    yield return StartCoroutine(m_GetUserToken("YourURL"));
    //You can now use the v_UserToken variable
    Debug.Log(v_UserToken);
}

2.You can use Action to wait for it.

The request function with Action:

IEnumerator m_GetUserToken(string url, Action<string> result)
{
    UnityWebRequest request = UnityWebRequest.Get(url);
    UnityWebRequestAsyncOperation async = request.SendWebRequest();
    while (!async.isDone) { yield return null; }

    if (request.isNetworkError || request.isHttpError)
    {
        Debug.Log(request.error);
        if (result != null)
            result(request.error);
    }
    else
    {
        Debug.Log(request.downloadHandler.data);
        if (result != null)
            result(request.downloadHandler.text);
    }
}

You can use then lambda to return the result:

void NonCoroutineFunction()
{
    StartCoroutine(m_GetUserToken("YourURL", (v_UserToken) =>
    {
        //You can now use the v_UserToken variable
        Debug.Log(v_UserToken);
    }));
}
Sign up to request clarification or add additional context in comments.

10 Comments

But when I'm yield before the data is downloaded and got handled by jsonutility the code where I started the coroutine runs forward to the processing and then I get a null error because processing happend before handling.
@DragonBale No. The Json code inside the else statement and below that while loop will never execute until the download is complete. Test this in the Editor and verify what I just said.
@Programmer this is true for code inside the coroutine, but not for code in the main loop AFAIK.
@Dschoni OP's code is a coroutine function. Notice the IEnumerator
@Dschoni Forget the thread talk. OP is confused like many other new Unity users. You do not need a Thread to download data with UnityWebRequest API. Notice how there is no single Thread code in the code from the question. People usually confuse Thread with coroutine in Unity. They are not the-same.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.