- Home /
Hold or Wait while Coroutine finishes
In the example below, how can I get FinishFirst() to complete first before running DoLast(), while still retaining the 'public void StartPage()' signature?
I'm trying to avoid making "StartPage()" return an IEnumerator as that would force me to change it in the interface. It would be great if my Interface for StartPage() supported both IEnumerator and Void without needing to implement both.
public void StartPage()
{
print("in StartPage()");
StartCoroutine(FinishFirst(5.0f));
DoLast();
print("done");
}
IEnumerator FinishFirst(float waitTime)
{
print("in FinishFirst");
yield return WaitForSeconds(waitTime);
print("leave FinishFirst");
}
void DoLast()
{
print("do after everything is finished");
}
In the future, please format your code with the code button. It takes 2 seconds.
sorry for that, actually I was looking for the "code" format button but didn't recognize it...will keep that in $$anonymous$$d, thanks.
Answer by Eric5h5 · Mar 16, 2012 at 02:40 PM
You would need to do "yield return StartCoroutine..." and have StartPage return IEnumerator. StartPage cannot return void in this case.
Answer by Nition · Aug 02, 2013 at 12:21 AM
I know this is an old question but the popular answer is wrong. It's using two Coroutines, DoLast is constantly checking to see if FinishFirst is done, and even then there'll be up to a 100ms delay after FinishFirst completes before DoLast is called.
Why not just call DoLast at the end of FinishFirst? If you don't know what method will need to be called at the end, you can pass it as an Action parameter:
public void StartPage() {
print("in StartPage()");
StartCoroutine(FinishFirst(5.0f, DoLast));
}
IEnumerator FinishFirst(float waitTime, Action doLast) {
print("in FinishFirst");
yield return new WaitForSeconds(waitTime);
print("leave FinishFirst");
doLast();
}
void DoLast() {
print("do after everything is finished");
print("done");
}
You can yield on FinishFirst, as I mentioned (but unfortunately did not elaborate on) in my answer. That would be the simplest and most flexible method.
public IEnumerator StartPage() {
print("in StartPage()");
yield return new StartCoroutine(FinishFirst(5.0f));
DoLast();
}
IEnumerator FinishFirst(float waitTime) {
print("in FinishFirst");
yield return new WaitForSeconds(waitTime);
print("leave FinishFirst");
}
void DoLast() {
print("do after everything is finished");
print("done");
}
The reason I didn't take that road was that in the original question he says "I'm trying to avoid making "StartPage()" return an IEnumerator as that would force me to change it in the interface." Otherwise yeah, that's the most logical.
Just a quick note for other people using this solution:
Do
yield return StartCoroutine (FinishFirst(5.0f));
I got an error when having new
between return and StartCoroutine. I now see that Eric5h5 have posted this in the answer below...
Answer by PaxNemesis · Mar 16, 2012 at 02:47 PM
You could either call DoLast() after the yield in the FinishFirst function or you could make DoLast a coroutine too with a loop that yields while the FinishFirst coroutine is running.
Something like:
bool inFirst = false;
public void StartPage() {
print("in StartPage()");
StartCoroutine(FinishFirst(5.0f));
StartCoroutine(DoLast());
print("done");
}
IEnumerator FinishFirst(float waitTime) {
inFirst = true;
print("in FinishFirst");
yield return new WaitForSeconds(waitTime);
print("leave FinishFirst");
inFirst = false;
}
IEnumerator DoLast() {
while(inFirst)
yield return new WaitForSeconds(0.1f);
print("Do stuff.");
}
thanks pax it halfworks, it will run whatever I put after the yield in DoLast() after FinishFirst(), but I also wanted to main StartPage() to hold as well (sorry I didn't state that). There is, as expected, a bit of concurrency with FinishFirst() and DoLast() (hence the statements needing to be put after the yield) that I was hoping to avoid...and I think that needing to use two StartCoroutines in this manner may be indicative of a redesign anyway. $$anonymous$$y appreciation for the answer, i still learned something!
If you want to hold the StartPage function you have to make that a coroutine too. And there yield where you want.
Answer by baddie · Jun 25, 2017 at 10:00 PM
Here's a simple extension class I wrote that adds a callback to UnityWebRequest.Send.
public static class NetworkingUtils
{
public static void Send(this UnityWebRequest request, MonoBehaviour behavior, Action onFinished)
{
behavior.StartCoroutine(routine(request,onFinished));
}
private static IEnumerator routine(UnityWebRequest request, Action onFinished)
{
yield return request.Send();
if (request.isError)
Debug.LogError(request);
else
{
Debug.Log("Results for " + request + ":\n" + request.downloadHandler.text);
onFinished();
}
}
}
Then you can use it by passing an action in to Send that will get called on completion. You'll also need to stick in a Monobehaviour so the coroutine works.
request.Send(monoBehavior, ()=>print("Done"));
You'll probably want to modify it to do more with errors (a different callback perhaps).
Answer by 5argon · May 24, 2017 at 11:29 AM
Much late, but if you want to check if a coroutine had finished or not instead of waiting and then run the next code sequentially, other than the obvious way of declaring an additional boolean flag you can use setup the IEnumerator
type variable instead of doing StartCoroutine(
on the method directly. (Which is the same, since the method returns IEnumerator
.
This variable is useful for StopCoroutine
, but if you set that variable to null at the end of the coroutine it can doubles as a way to check if a coroutine ended or not by null checking it.
I use this so players cannot input while the story is advancing. For example, defines :
private IEnumerator advanceStoryDelayRoutine;
IEnumerator AdvanceStoryDelayRoutine()
{
yield return new WaitForSeconds(3.5f);
advanceStoryDelayRoutine = null;
}
When using it
//this check is necessary for the first time or StopCoroutine will stop to nothing, resulting in a run time error
if(advanceStoryDelayRoutine != null)
{
StopCoroutine(advanceStoryDelayRoutine);
}
advanceStoryDelayRoutine = AdvanceStoryDelayRoutine();
StartCoroutine(advanceStoryDelayRoutine);
And then the input code can check against this variable
if(advanceStoryDelayRoutine != null)
{
//still hasn't finished yet
}
Doesn't WaitForSeconds
only take float as an argument?.
Yeah, sorry. That advanceStoryDelayTime
was defined out of pasted code. That variable is indeed a float type. Edited to constant value.
Yeah good answer, I've used that same system quite often. Worth noting that you do have to set the coroutine you've assigned manually back to null after the coroutine is done (which you've done in your example, it's just good to mention for others) - it doesn't go back to null automatically when it's over.