Need to call yield TWICE ??? (ANSWERED)

A great big thank-you to ALDONETTO who actually did find the actual answer to this actual question, thank you very much Aldo.

The solution is simply that the print-to-the-console statement is buggy / behaves strangely.

Simply change the test routine to this form:

var someThing:Transform;
function runMeOneChunkAtATime()
    {

    for(x=0;x<100000000;++x) r += Mathf.Sqrt(x+r);
    print("aaa");
    someThing.position.x +=50;     // add this line
    yield;                         // just the one needed

    etc...
    }

And, thanks to Aldonetto, you will see that it performs perfectly and consistently in moving the object - while the print statement perfectly and consistently exhibits the strange behaviour explained below. (You will see the two things matching and then not matching in time.)

An interesting practical upshot of this:

If you are using the print statement to debug time-based programming, it does not work reliably. BUT if you add the bizarre “double-yield” as a quick fix during development – in fact the print statement will then effectively work properly for you during development.

Once again thank you very much Aldonetto for the awesome find. Thanks.


If you are truly an expert with yield and coroutines … perhaps you can explain this!

In Javascript/Unityscript. Make a testing button …

function OnGUI()
	{
	if (GUI.Button (Rect (400,10,70,50), "TEST"))
		{ runMeOneChunkAtATime(); }
	}

Now the following test routine …

function runMeOneChunkAtATime()
	{
	var r:float;
	var x:int;
	for(x=0;x<100000000;++x) r += Mathf.Sqrt(x+r);
	print("aaa");
	yield;
	yield;
	
	for(x=0;x<100000000;++x) r += Mathf.Sqrt(x+r);
	print("bbb");
	yield;
	yield;
	
	for(x=0;x<100000000;++x) r += Mathf.Sqrt(x+r);
	print("ccc");
	yield;
	yield;
	
	for(x=0;x<100000000;++x) r += Mathf.Sqrt(x+r);
	print("ddd");
	yield;
	yield;
	}

Now - why have double yield statements in each position?

First: put just ONE yield statement in each place, as you wold expect, and run it.

Notice it “doesn’t work” - the first one “does not pause”. Notice ‘aaa’ and ‘bbb’ appear on the console with no time gap between.

Now try it with double yield in each position. Notice the behaviour is perfectly what you would expect.

How to explain?

I tested this script, and it performed the same with one or two yield (at least in my PC notebook).

Anyway, if you don’t want to stop Unity while a long routine executes, you must place yield instructions at well chosen places. Simple yield instructions like those in your code just stop coroutine execution and return to the calling routine - but an internal control structure remains active, and in the next update cycle the coroutine is automatically resumed in the instruction after the yield (that’s why @dannyskim was talking about frames).

If you must call some slow function 10000 times at some point of your game, for instance, and want to span the loop over several frames, you can use this:

for (var i = 0; i < 10000; i++){
  SlowFunction(i);
  yield; // let Unity free until next frame
}

But since each yield releases Unity up to the next frame, this will take 10000 frames to execute! You should thus find a good compromise - maybe yield at each 100 iterations:

var n = 0; 
for (var i = 0; i < 10000; i++){
  SlowFunction(i);
  if (++n == 100){ // reached 100?
    n = 0;
    yield; // let Unity do other jobs until next frame
  }
}

Since this is a coroutine, control will return to the caller code immediately (or in the first yield, I’m not sure), but it will keep running behind the scenes, thus you can do anything you want while the long routine executes.

NOTE: Yield cannot be used in all routines! The docs mention Update and FixedUpdate, but other periodic functions like LateUpdate and OnGUI can’t be coroutines as well. Another limitation: a coroutine can’t return anything (it actually returns IEnumerator, thus no other value can be returned).

EDITED: I tested the script again with several combinations of yields, and it produced weird and unexpected results for each combination. I also tested it in a C# version, but the results were the same. It’s really frustating: you just can’t predict what the hell it will do in each case!

But the problem is actually caused by the sync between the console and the update cycles: Unity performs as expected, resuming after the yield instruction in the next frame, but the console is updated at weird intervals.

The script below shows the actual time and frame count, but the first two lines appear at the same time, while the others seem to appear at the right intervals:

function Update(){
    if (Input.GetButtonDown("Fire1")){
        runMeOneChunkAtATime();
    }
}

var startTime: float;
var startFrame: int;

function PrintTime(id: String){
    print(id+" T= "+(Time.realtimeSinceStartup-startTime).ToString("F3")+" F= "+(Time.frameCount-startFrame));
}

function runMeOneChunkAtATime(){

    var r:float;
    var x:int;
	
    startTime = Time.realtimeSinceStartup;
    startFrame = Time.frameCount;
	
    for(x=0;x<100000000;++x) r += Mathf.Sqrt(x+r);
    PrintTime("aaa");
    yield;

    for(x=0;x<100000000;++x) r += Mathf.Sqrt(x+r);
    PrintTime("bbb");
    yield;

    for(x=0;x<100000000;++x) r += Mathf.Sqrt(x+r);
    PrintTime("ccc");
    yield;

    for(x=0;x<100000000;++x) r += Mathf.Sqrt(x+r);
    PrintTime("ddd");
}

The values updated at the right times when a GUIText was used instead of print.

NOTE: I finally found the answer to my own question: the coroutine returns control to the caller when the first yield is executed, not immediately.

Well, when utilizing yield inside of a java script, I believe it’s default application would be to yield for one frame. So when using two yield statements, you are now yielding for two frames.

Using yield by itself and waiting for one frame, or trying to control waiting by utilizing frames in my opinion can give you varying results in varying situations, hardware, processing allocation, etc.

Is there a particular reason you’re not using

yield WaitForSeconds(amount);

Or if you really want to utilize waiting for one specific frame, maybe you should try using

yield new WaitForFixedUpdate();

for more predictable results?

May I ask what, exactly, you expect your code to do? Remember that when coding in UnityScript, what you see isn’t actually what you get. In this case, the line

runMeOneChunkAtATime();

Will be translated into

StartCoroutine(runMeOneChunkAtATime());

at compile time!

Would this explain the inconsistencies you’re experiencing? This is one of the big reasons why I don’t like the JavaScript implementation in Unity, btw- there’s too much magical stuff going on that is really not obvious to the user.