- Home /
question about Coroutines and waiting for a function to finish
Let's say I have a function "f" that needs to wait until "g" is finished it's execution, but "g" has multiple yield returns in it. Something like:
void f() {
yield return StartCoroutine(g());
// do something else now...
}
IEnumerator g() {
// do something
while (something)
yield return null;
// so something else
yield return null;
}
So in this case, I don't want f() to continue until the final "yield return null" has been called in g(). But my understanding is that f() will continue after the very first time "yield return null" has been called.
I suppose the while loop in g() could just be empty. That would be a fix. But is there ever a case where we need multiple "yield return null" statements in a function, but need to "wait" for the last one?
Thanks for any insights into this. I've read the following:
http://docs.unity3d.com/Documentation/ScriptReference/index.Coroutines_26_Yield.html
But I don't find it to be the most helpful piece of documentation. It uses the statement: "The yield statement is a special kind of return, that ensures that the function will continue from the line after the yield statement next time it is called." But that doesn't make sense to me, because the function WILL continue whether or not it is called again!
Thanks.
Regarding my last paragraph, I now understand that the function continues after the end of the current frame. So this suggests that there may be situations where the above code would be needed. I want the while loop to pause until the end of each frame (until some global condition is met), but I still only want f() to continue after g() has finished completely.
Answer by Bunny83 · Jun 28, 2012 at 03:07 PM
If you yield return another coroutine like you did, it will wait until the coroutine is finished, but i guess you have trouble to understand what a coroutine is.
A coroutine is not a function that runs in some magic parallel thread. The yield statement is actually a C# feature. Functions that contain this statement have to return an IEnumerator. Such a function is called a generator-function. When you call the function it doesn't execute any of the code in it. It just returns an object that implements the IEnumerator interface. This object can be used to execute the actual code until the next yield statement.
In Unity you pass the result of the generator to StartCoroutine. This function will store the given object internally on the MonoBehaviour instance and will take care of executing it.
The coroutine scheduler in Unity will drive the execution when it's time to do so.
When you yield return in a coroutine, the coroutine is exited at this point and will return the given object to the scheduler. WaitForSeconds is just a class that holds a float value. The scheduler will check what the coroutine returns and "schedule" the coroutine for continue when it's time to do so. When you yield another coroutine (a Coroutine is returned by StartCoroutine), the scheduler will suspend the execution until the yielded coroutine has finished.
So in your case the coroutines should look like this:
IEnumerator f() {
Debug.Log("f() started");
yield return StartCoroutine(g());
Debug.Log("f() is done");
}
IEnumerator g() {
Debug.Log("g() started");
while (something)
yield return null;
Debug.Log("g() is done");
}
// Somewhere else you start f() like this:
StartCoroutine(f());
this will print:
"f() started"
"g() started"
"g() is done"
"f() is done"
If you still have trouble to understand how they work, take a look at this custom CoroutineScheduler. The author also have this great blog which explains a bit what happens behind the scenes
Shoot! I haven't finished digesting your response yet, but I realized that I had the wrong return-type for the g() function. I've edited that.
Your explanation is excellent. Let's see if I understand correctly: for the above to work as you described, then the f() function must also be a coroutine so that it gets suspended until g() is finished. If I just have:
void f()... IEnumerator g()...
Then that won't work, because f() won't be suspended. Am I close?
Yes, if you change f() to be public void f()
it would start coroutine g(), then continue with f giving you
f() started g() started f() is done g() is done
making f() type IEnumerator and calling yield return g() basically is stopping f() at the yield until g() returns, then when g() completes and returns, f() will continue from that line (not beginning at the top). It's kind of like saying " wait here until this finishes, then continue" rather then, if you just start a new coroutine (g()) from a regular function its saying " go do this thing independently but continue running without waiting for a response". I know this is a rather untechnical description of it but it might help.
Your answer
Follow this Question
Related Questions
Coroutines and states 1 Answer
Are coroutines freezing my game? 0 Answers
How do I properly use Coroutines, yield and animators to sequence commands ? 2 Answers
Inconsistent StopCoroutine behavior with CustomYieldInstruction 1 Answer
Problem with Stopping Nested Coroutines: Control Never Returned to Parent 3 Answers