- Home /
Invoke and Yield fail together- bug or feature?
So I ran into an issue, had some code that was working fine, called a method with Invoke on a delay. Extended the method being called and it stopped working; no errors, no warnings, the method just never got invoked. With some testing I managed to determine the offending new line: "yield WaitForSeconds(1);" Remove the yield, and it works fine.
So I put together a simple test, new scene, just attached the following script to MainCamera:
function Update()
{
if (Input.GetButtonDown("Fire1"))
{
Invoke("testy",1);
}
if (Input.GetButtonDown("Fire2"))
{
Invoke("testy2",1);
}
if (Input.GetButtonDown("Fire3"))
{
Invoke("testy3",1);
}
}
function testy()
{
print("testy entered");
yield WaitForSeconds(5);
print("testy ended");
}
function testy2()
{
print("testy2 entered");
//yield WaitForSeconds(5);
print("testy2 ended");
}
function testy3()
{
print("testy3 entered");
while(false)
{
yield WaitForSeconds(0);
}
print("testy 3 exited");
}
Tested this in the editor on both windows and mac, same behavior each time. The invoke on testy2 works perfectly. The other two do not. Note, they don't just not execute fully; they are never called at all, even the "entered" lines do not print.
testy3 is perhaps the revealing case - the yield can by definition never happen, since the condition is literally "false." As I said initially, there are no errors or warnings displayed in the output, either while compiling or running. It's just silently failing.
Thought I'd ask the community, make sure this isn't just an "undocumented feature" or a known issue before I submit a bug report with this simple repro case.
:edit: Thought I'd add the simple hackey workaround I'm currently using, for others who might find this question. There was a similar question already, but their solution was moving the yield out of the invoked function - which is not an option in my case.
I just added a function, DelayInvoker, which takes a string and int as params, yields WaitForSeconds(int), and then calls StartCoroutine on the string. Replaced the Invoke calls with calls to DelayInvoker, works as expected.
If someone has a more elegant workaround, by all means share.
went ahead and built a project with just the one scene and script and submitted it as a bug report, as at the very least if Invoke and yield are meant not to be compatible, it should be giving some warning or error message.
Answer by Eric5h5 · Nov 19, 2011 at 11:16 PM
It's not a bug. Invoke doesn't have any StartCoroutine functionality built in, so no, you can't Invoke coroutines. I don't know how feasible it would be to implement an error, because Invoking a function that happens to be a coroutine is still perfectly valid, it just won't work as a coroutine. (A coroutine is a function that returns IEnumerator.) It may be easier to see this in C#, since JS automatically uses StartCoroutine when you call a function that's a coroutine. In C#, you have to explicitly use StartCoroutine--calling a coroutine the way you would call a "normal" function has the same effect as using Invoke on a coroutine. (i.e., it won't work as a coroutine.)
For what you want, there would have to be an InvokeCoroutine function, which of course doesn't exist. But rather than Invoking a function which starts a coroutine, you can emulate Invoke's functionality in a coroutine using WaitForSeconds instead:
function Update()
{
if (Input.GetButtonDown("Fire1"))
{
StartCoroutine("Testy", 1);
}
}
function Testy (delay : float)
{
yield WaitForSeconds(delay);
print("testy entered");
yield WaitForSeconds(5);
print("testy ended");
}
In this way you can use StopCoroutine("Testy")
to implement the functionality of CancelInvoke.
ah, I see, thanks for the explanation. I don't see why there could not be a warning, though. Surely at some point it's detecting the mismatch and making the choice not to make the call, I don't see why it could not throw an error or warning at that time?
:Edit: misread the fix initially, changing rest of comment.... re: the fix... that will work fine in my case, and I'd actually tried it, but the middleman function just seemed like a more generic work-around for what seemed like a bug, that didn't require changing the methods themselves.
Surely at some point it's detecting the mismatch and making the choice not to make the call
No, there isn't any mismatch, it's just running the function the same way it runs any other function. As I said, it's the same as running a coroutine without explicitly (or implicitly as in the case of JS) calling StartCoroutine.
but... no. It is not running the function at all. Nothing inside the function is executed. ?
Yes, as I said, that's what happens when you run a coroutine without using StartCoroutine.
... All right, I guess I just have to accept that. Doing nothing seems reasonable enough as defined behavior since what I was asking it to do was impossible. I don't comprehend how it could possibly not have any opportunity to generate an error or warning, though. I could see it not detecting this at compile time, but at runtime, at some point it tries to invoke that function, finds a coroutine, and does nothing ins$$anonymous$$d. I don't see why this would be somehow impossible to detect and report, as it's perfectly able to report if it finds the specified function does not exist at all.
Your answer
Follow this Question
Related Questions
Invoke/yield in non MonoBehaviour classes 1 Answer
Are coroutines freezing my game? 0 Answers
How to Invoke() .. one frame 2 Answers