zoenightshade
zoenightshade

Reputation: 159

Coroutine in foreach loop Unity3D C#

everyone!

I'm trying to upload multiple files using foreach. So far I'm successful with uploading everything but I want to finish the coroutine first before proceeding to the next item in the for loop bc I'm gonna make a progress bar. For some reason, the coroutines are all being executed all at one instead of one by one.

Here is my code:

    public void CallUploadFile() //Called by button
    {
        StartCoroutine(UploadScene());
    }

    IEnumerator UploadScene()
    {
        foreach(string str in item_list)
        {
            string item_path = str;
            yield return new StartCoroutine(StartUpload (item_path));
        }
    }

    IEnumerator StartUpload(string item_path)
    {
         byte[] image_bytes;
         image_bytes = File.ReadAllBytes(item_path);

         // upload using WWWForm;
        WWWForm form = new WWWForm ();
        form.AddBinaryData ("scene[image]", image_bytes);
        using (UnityWebRequest www = UnityWebRequest.Post("somelink.com/upload", form))
        {
             yield return www.Send();
            while (!www.isDone)
            {
               Debug.Log(www.progress);
               yield return null
            }
            Debug.Log("Success!");
        }
    }

I already read regarding this topic on various forums and this code should work but for some reason this is not working properly. Any heads up would be much appreciated.

Upvotes: 1

Views: 4707

Answers (3)

Everts
Everts

Reputation: 10721

I think your code is working fine but you are printing wrong.

IEnumerator UploadScene()
{
    foreach(string str in item_list)
    {
        string item_path = str;
        yield return new StartCoroutine(StartUpload (item_path));
    }
}

You are yielding correctly to start each coroutine once the previous is over. I would say so far so good.

IEnumerator StartUpload(string item_path)
{
     // Code
    using (UnityWebRequest www = UnityWebRequest.Post("somelink.com/upload", form))
    {
         yield return www.Send();
        while (!www.isDone)
        {
           Debug.Log(www.progress);
           yield return null
        }
        Debug.Log("Success!");
    }
}

You are yielding on the Send request, and this will somehow "stall" the coroutine at this point until the download is fully done. Next, if not done, print but you yielded until the end of the download.

So I expect you are getting only success printing.

What you need is to yield inside the loop:

        www.Send();
        while (!www.isDone)
        {
           Debug.Log(www.progress);
           yield return null
        }

this will call for the Send request and will continue. But now it will yield in the while loop util done so it will print the progress one by one.

Keep in mind that this will make your download time longer. You could trigger them all in the same loop, then Unity will start different threads (not coroutines, proper threads) so you can have many download in parallel.

Upvotes: 1

Anders
Anders

Reputation: 17564

Your outer enumeration can yield the inner one like

IEnumerator UploadScene()
{
    foreach(string str in item_list)
    {
        string item_path = str;
        yield return StartUpload (item_path);
    }
}

edit: I cant find the send method in the docs, https://docs.unity3d.com/ScriptReference/Networking.UnityWebRequest.html

Are you sure it returns a yield instruction? You can probably skip that and just use the yield return null while not done code

edit2: There is a async SendWebRequest method. Its return value is not compatible with Coroutines.

Upvotes: 1

David
David

Reputation: 16277

Use below code: this makes all the code finishes before null is returned, which fits into your needs.

IEnumerator UploadScene()
{
    foreach(string str in item_list)
    {
        string item_path = str;
        StartUpload (item_path));
    }
    yield return null;   //returns until work is done.
}

void StartUpload(string item_path)
{
     byte[] image_bytes;
     image_bytes = File.ReadAllBytes(item_path);

     // upload using WWWForm;
}

Upvotes: 1

Related Questions