Coroutines / Async calls

Hello there Unity Community!

I am currently working on an application which makes use of web API calls. The required information is obtained from the contents within the html body tags. For this I’ve been using coroutines. In the current implementation we have in each of the classes a coroutine for calling the website and performing operations. But what I would like to do is, instead of having all these coroutines basically doing the same, make one class for performing the routines and return the string to the method which requested the routine.

I’ve tried to put in a while loop to wait for the result to be returned (line 26). But instead of and looping through the while and checking whether the request has been handled. It just remains stuck inside the loop checking whether it’s state had changed.

I also tried using async await functionalites. But in order to execute a WWW call you need to make use of a coroutine. And I Couldn’t get a coroutine to work inside a Task of an async call.

I’ve seen many implementations online which only modify / work within the same class. And that works fine. But I need something to make a call from another class to one another and return a variable to the method it being invoked from.

public class WebRequestHandler : MonoBehaviour
{
    public static WebRequestHandler WRH;
    private bool reqComplete = false;
    private string fetchedData = null;

    private void Awake() {
        if(WRH == null) {
            // if not, dont destroy it on load, this keeps the instance through the difference scenes
            DontDestroyOnLoad(gameObject);
            WRH = this;
        }else if(WRH != this) {
            // if there is allready one, destroy the new one that's trying to be created
            Destroy(gameObject);
        }
    }


    public string WWWRequest(string API_Method, WWWForm form)
    {
        fetchedData = null;
        WWW www = new WWW(API_Method, form);
        StartCoroutine(APIFetchRequest(www));
      
        while (!reqComplete) {
            /*Wait to complete or return error.*/
            Debug.Log("Waiting...");
        }
        // Reset Request Completion boolean for the next request.
        reqComplete = false;
        return fetchedData;
    }
 
private IEnumerator APIFetchRequest(WWW request)
{
    yield return request;
    if(request.error != null) {
        Debug.Log("[WebRequestHandler] API Fetch error: " + request.error);
    } else {
        //strip all unnecessary tags & content.
        Regex regex = new Regex("<body>(.+?)</body>");
        fetchedData = regex.Match(request.text).Groups[1].Value;

        //Set operation completed.
        reqComplete = true;
        }
    }
}

I hope one of you might have an answer to this question!
Thanks in advance!

  • Mark

It doesn’t work like that. Your while loop is spinning forever.
When you call a coroutine, it runs up until its first yield, after which point it returns execution to the caller.

You can pass a callback to a coroutine, so you can run it upon success. I think that would work well for your situation, with: System.Action.

1 Like

Building on what @methos5k said, this is definitely the way to go. Structure your coroutine to accept the URL and give it a success callback (System.Action success) and if you like a failure callback (System.Action error), and then you can keep all the WWW silliness localized to the coroutine.

WWW objects also implement IDisposable, which means you need to Dispose them, or use them within a using() block, otherwise you risk leaking resources.

Here’s a super-simple example:

    IEnumerator WGET(
        string url,
        System.Action<string> OnSuccess,
        System.Action<string> OnError)
    {
        using (WWW www = new WWW( url))
        {
            yield return www;

            if (www.error == null)
            {
                OnSuccess (www.text);
            }
            else
            {
                OnError (www.error);
            }
        }
    }

Usage would be something like:

        StartCoroutine( WGET( "http://www.google.com",
            (data) => {
                Debug.Log( "Got:" + data);
            },
            (err) => {
                Debug.LogError( "Error:" + err);
            }));