- Home /
Yield position within while loop in coroutine
I have this code:
public bool A = true;
public bool B = true;
public void Initialise()
{
StartCoroutine(Appear());
}
public IEnumerator Appear()
{
yield return StartCoroutine(TestA());
Debug.Log("Should only be one of these");
yield return StartCoroutine(TestB());
}
public IEnumerator TestA()
{
while(A)
{
Debug.Log("A");
yield return null;
}
}
public IEnumerator TestB()
{
while(B)
{
Debug.Log("B");
yield return null;
}
}
Can someone explain why the output looks like this? Why are there two instances of a single "A" and "B"? (I manually uncheck "A" and "B" in sequence in the inspector panel while the game is running to execute this test if you were wondering)
Also, if I move the yield return null;
to be above the debug output
public IEnumerator TestA()
{
while(A)
{
yield return null;
Debug.Log("A");
}
}
the resulting debug output is as expected:
I am at a loss as to how the thread executes and why the initial debug output is considered separate from the others. Any help would be appreciated.
Answer by Bunny83 · Sep 29, 2019 at 10:01 PM
That's pretty easy to explain. When you start a coroutine you actually create an IEnumerator instance and hand it over to unity by passing it to StartCoroutine. StartCoroutine will immediately run your coroutine one step up to the first yield statement. At this point the coroutine will be queued into the normal coroutine scheduler and depending on the value yielded it will decide when to re-schedule it. After this the StartCoroutine call will actually return.
So the code that comes after your StartCoroutine call will resume once the started coroutine reaches it's first yield statement. The reason why the debug log shows up as a seperate entry is the different stacktrace. The first call comes from your direct call of StartCoroutine. All subsequent calls of your Debug.Log calls come from the internal coroutine scheduler so it has a different stacktrace. Just have a closer look at the two logs (the lower section of the log window when you select one of the messages.)
edit
Please note that coroutines are not threads. They implement cooperative multitasking. All coroutines run on the main thread. Unity just utilizes the concept of C# iterator blocks. The C# compiler will turn a method that contains yield statements into a generator method which when called doesn't execute a single line of your code but instead it creates an instance of a compiler generated hidden class. This class instance is actually a statemachine object that represents your coroutine code. This object has a MoveNext method which when called will execute all code between the last yield and the next one and then return. So all Unity actually does is when you start a coroutine, Unity will call MoveNext once from inside StartCoroutine which will move the statemachine to the first yield statement. The iterator object will be stored internally by the coroutine scheduler.
As you can see in the flow chart all pending coroutines which have yield one of the common values (null, WaitForSeconds, WWW or chained coroutine calls) will actually be resumed just after your Update callback when they are due.
If you want to know more about iterator blocks and how they work, I recommend this blog post. If you want to get a better understanding how the coroutine scheduler works, have a look at this custom coroutine scheduler implementation on the Unity wiki.
Your answer
Follow this Question
Related Questions
How To Create Coroutine Delegates? 1 Answer
C#: Use of while loops inside of a coroutine 2 Answers
StartCoroutine important for using yield? 1 Answer
JS Wait for a function to complete - no WaitForSeconds 1 Answer
Coroutines and states 1 Answer