- Home /
How do I return a value from a coroutine?
I have a popup message box which is driven by a yield function. This yield waits until a button is hit (confirm or cancel). When a button is hit I want to retun the button type that was hit to the point at which the initial popup message box was called.
I know there's someting about co-routines only returning IEnumerators but I can't seem to get that working. ..and I certainly cant do returnValue= yield myCoroutine(). So how does this work?
Cheers
So is the no way of getting a value from a StartCoroutine(). Ive got my StartCoroutine to work but I can only get it to print the value I want. I find it annoying that, I can see that value I want printing but i cant access it??
Answer by Ted-Bigham · May 14, 2015 at 12:31 AM
A Unity Coroutine is a C# generator. Every time you call yield in a generator you are "returning" a value. That's what they are intended to do, generate values. The problem isn't about returning values from a Coroutine, because you're doing that. The problem is getting access to those values that the Unity engine absorbs.
You do it by running the generator yourself instead of letting Unity do it, and exposing the yielded value using a public property.
Create a utility class for invoking coroutines...
public class CoroutineWithData {
public Coroutine coroutine { get; private set; }
public object result;
private IEnumerator target;
public CoroutineWithData(MonoBehaviour owner, IEnumerator target) {
this.target = target;
this.coroutine = owner.StartCoroutine(Run());
}
private IEnumerator Run() {
while(target.MoveNext()) {
result = target.Current;
yield return result;
}
}
}
Put a yield at the end of your coroutine with the value you want to return...
private IEnumerator LoadSomeStuff( ) {
WWW www = new WWW("http://someurl");
yield return www;
if (String.IsNullOrEmpty(www.error) {
yield return "success";
} else {
yield return "fail";
}
}
Invoke it and get the return value using the utility class...
CoroutineWithData cd = new CoroutineWithData(this, LoadSomeStuff( ) );
yield return cd.coroutine;
Debug.Log("result is " + cd.result); // 'success' or 'fail'
Notice you need to yield to cd.coroutine not cd. If you tried to yield to cd, the code would only wait one frame, even if the coroutine is not finished.
Damnit Ted how's anyone supposed to find this if it's buried 5 replies deep?
Give it time Jeff. The good answers will bubble up to the top. :D
But, don't you then need to run the last 3 lines in a coroutine because you have to use a yield statement anyway, leaving us in the same predicament?
@crash664. The last 3 lines must be run from a coroutine. The question isn't about running outside a coroutine, it's about getting data from a coroutine you've yielded to. This question generally comes up when you want your coroutine code to be self contained with no "hooks" to other code, but still return a value.
Now that Unity supports CustomYieldInstruction, I believe this code can be improved.
CoroutineWithData can probably extend CustomYieldInstruction, overriding keepWaiting( ) to check if the internal coroutine has completed. That would allow the calling code to call yield directly on the CoroutineWithData instance.
The example would then be "yield cd" ins$$anonymous$$d of "yield cd.coroutine". It's cleaner and less error-prone.
In the example you gave, one can do without CoroutineWithData class, actually. After cd.croutine is finished and control is returned to the calling method, you can simply access cd.coroutine.Current property and get the value. In other words, you could save pointer to routine ( var routine = LoadSomeStuff();
), yield it ( yield return routine;
) and then fetch the value ( Debug.Log("result is " + routine.Current;
). Proposed class would be useful in case we StartCoroutine(LoadSomeStuff());
ins$$anonymous$$d of yield return routine;
, but then i personally find it more convinient to use compact lambda callbacks.
Useful solution, thanks Ted. Here I did a customisation from Ted's script, hope its helpful for someone. Basically I replaced object to a generic type, so you don't have to do casting by yourself.
Source script(From Ted's CoroutineWithData): using UnityEngine; using System.Collections; using System.Collections.Generic;
public class CoroutineWithData<T>
{
private IEnumerator _target;
public T result;
public Coroutine Coroutine { get; private set; }
public CoroutineWithData($$anonymous$$onoBehaviour owner_, IEnumerator target_)
{
_target = target_;
Coroutine = owner_.StartCoroutine(Run());
}
private IEnumerator Run()
{
while(_target.$$anonymous$$oveNext())
{
result = (T)_target.Current;
yield return result;
}
}
}
Example:
CoroutineWithData<$$anonymous$$yClass> coroutine = new CoroutineWithData<$$anonymous$$yClass>(this, FindLargest(3, 5));
yield return coroutine.Coroutine;
IEnumerator FindLargest(int a, int b)
{
if(a > b)
yield return new $$anonymous$$yClass(true);
else
yield return new $$anonymous$$yClass(false);
}
Answer by harleywinks · May 22, 2015 at 12:42 AM
The simplest way is to use a closure/callback function, like if you wanted to return a bool, you could use the below...
void Foo()
{
StartCoroutine(Bar((myReturnValue) => {
if(myReturnValue) { ... }
});
}
IEnumerator Bar(System.Action<bool> callback)
{
yield return null;
callback(true);
}
...obviously, you could have the parameter[s] for the callback be any type you want.
I love this approach way more than the java style setting up a class tree mess for a single callback. Both options are totally correct and valid, I just prefer this one!
I believe this is the best answer. Simple, working and with a nice trick with callback!
This is the perfect answer for me!!! Simple and do what is needed. Below is a sample of the way a used:
void Foo()
{
StartCoroutine(Timer$$anonymous$$anager.Instance.cTimer(1, 3, returnValue =>
{
print(returnValue);
}));
}
public IEnumerator cTimer(int _initTime, int _endTime, System.Action<float> callback=null)
{
print("Started");
float timer = _initTime;
while (true)
{
timer += Time.deltaTime;
callback(timer);
if (timer >= _endTime)
{
yield break;
}
yield return new WaitForEndOfFrame();
}
}
Answer by jashan · Aug 10, 2010 at 04:36 PM
You can't directly return values from Coroutines because what Coroutines actually are is an enumerator which can be used e.g. in a foreach-loop (that's what these IEnumerators and the yield was originally created for). So, Unity executes a part of the Coroutine until it hits
yield return <something>;
(that's C# syntax, and I highly recommend using C# because it really gives you much more of an understanding of what's actually going on there).
When such a statement is hit, control is returned to the caller of the method (which is some part of the Unity engine that manages coroutines). Until Unity decides to "grab the next item from the enumeration" ... which depends on what you return (e.g. WaitForSeconds would let Unity wait an amount of frames until the given number of seconds have passed).
Coroutines are not really asynchronous but in many ways they appear to be and from the perspective of returning values, they are: How could you return a value to a piece of code that gets executed after the Coroutine was started? You'd have to "inject" the return value somehow back ... won't work ;-)
So what you usually do with asynchronous programming is to have the method that's asynchronously called (in this case your coroutine) write the relevant return values into a class so that others can read them on demand. For that purpose, you could use a member variable in your script that also implements the coroutine. In fact, that would be the simplest solution. Then you can simply poll for that value (e.g. in each Update) and act whenever the value has changed (or when there is a value at all).
Answer by Mr-JWolf809 · Sep 26, 2016 at 12:59 PM
If you already are in a coroutine and wants the result from a different coroutine, you do this:
public void Start()
{
StartCoroutine(DoingTheWork());
}
/// <summary>
/// The Coroutine who wants the result from another coroutine
/// </summary>
public IEnumerator DoingTheWork()
{
int result = 0;
yield return Run<int>(CalculationCoroutine(), (output) => result = output);
Debug.Log("The result is: " + result);
}
/// <summary>
/// The coroutine doing a calculation/action over time
/// </summary>
public IEnumerator CalculationCoroutine()
{
int result = 0;
for (int i = 0; i < 100; i++)
{
result += i;
yield return new WaitForSeconds(0.1f);
}
yield return result;
}
public static IEnumerator Run<T>(IEnumerator target, Action<T> output)
{
object result = null;
while (target.MoveNext())
{
result = target.Current;
yield return result;
}
output((T)result);
}
Thank you. This belongs in the Unity docs, for sure.
It is a very interesting way of doing things, where you can essentially rely on yield to happen post callback, to set a local var w/ the data from the callback, and then because of the behavior of yield you are able to get a value. It is just a very unintuitive async control flow mechanism compared to async/await, but it isn't actually that different from what async/await is doing internally - essentially creating a small state machine.
Thanks for putting a clear picture of this up - it makes sense, but it wasn't clear that this was possible.
Answer by Wolfram · Aug 10, 2010 at 03:30 PM
Do you mean something like this?
var buttonHit:bool=false; var didConfirm:bool=false;
function OnGui(){ if(GUI.Button("Confirm")){ buttonHit=true; didConfirm=true; } if(GUI.Button("Cancel")){ buttonHit=true; didConfirm=false; } }
function DoWait(){ while(!buttonHit) yield; if(didConfirm) print("user has confirmed action"); else print("action was cancelled!"); }
EDIT: In this variant, the coroutine handles everything - so you don't initiate a coroutine just to wait for a button click. Instead, you're already inside the coroutine. (Note: Not included is the section making sure your GUI message box is visible only when you want it to be):
function HandleMessageBox(){
ShowMessageBox();
while(!buttonHit)
yield;
HideMessageBox();
if(didConfirm)
print("user has confirmed action");
else
print("action was cancelled!");
}
That's certainly one way of doing what I described but I really want to know if I can use the return(); statement in some way.ie in your DoWiat function want to have something like return(didComfirm); which gets returned to wherever the DoWait function was called from. Get me?
I try to avoid UnityScript, we're doing everything in C#. However, I don't think in either language a coroutine can return something other than an IEnumerator. However, to answer your comment, DoWait() is a coroutine, so the call to it will return immediatedly, and the code that called DoWait() will continue - you cannot "return" to that point after you pushed a button, that's the whole point of making it a coroutine call. But I'll edit my answer to include another possibility - doing everthing within the coroutine.
This might be helpful, too: http://answers.unity3d.com/questions/9498/how-to-properly-use-yield-for-a-coroutine
What you always can do is this: Declare a global variable at beginning of the script and access it from both: $$anonymous$$ain-function and co-routine. For example: The co-routine changes the variable and the main-script is always reading it.
Your answer
Follow this Question
Related Questions
Pause the coroutine inside a loop 1 Answer
Corutine not behaving like it should? 1 Answer
In coroutin function,if(x) { yield return a } . if(y) { yield return b }. Is it possible? 0 Answers
How do I return a reference to an object downloaded via a function? 0 Answers
Coroutoutine doesnt work properly. 3 Answers