views:

317

answers:

8

I'm having trouble making something happen over and over without a for loop. Take a look at this:

package {

    import flash.display.Sprite;
    import flash.events.Event;

    public class Main extends Sprite {

        public function Main() {

            addEventListener("done", caller);

            caller();            

        }

        public function caller(e:Event = null):void {

            trace("hello!");

            dispatchEvent(new Event("done"));

        }

    }

}

sing this will get you an "Error #2094: Event dispatch recursion overflow." really fast. It will show that the event dispatcher and caller() are getting called inside of them selves, nesting until the error happens.

What I want to do is this: "When caller() is done, call it again" not: "call caller() before it finishes"

Now, before people start suggesting using a timer to guess how long it will take or use ENTER_FRAME, This caller() will not have any graphic data and won't be connected to a Sprite and the time it takes to finish may vary greatly from call to call. I'm really looking for a way to run it only after it has completely finished.

Thanks for your help.

A: 

Is this for an assignment?

If you don't want for loops, how about a while loop?

Trying to use timers could work but it gets messy. If you absolutely must user a Timer then have some boolean flag set to true/false if your function is still running. The timer event would see if your function is finished, if so then call it again.

Jake
A: 

can't use for loops or while loops because the work is so link the program times out. I've been told timers won't call the function again if it's still running but I'm looking for confirmation.

A: 

I would use enterFrame... Flash is frame based... when your process is finished, you check if you still have time for another call to the function, if not, just wait for the next frame to come...

addEventListener("enterFrame", loop);
function loop(e) {
   var maxtime=1000/stage.frameRate;
   var t1=getTimer();
   while(getTimer()-t1 < maxtime) {
      myProcess();
   }
}
Cay
A: 

OK, I know you said

his caller() will not have any graphic data and won't be connected to a Sprite

And

I'm really looking for a way to run it only after it has completely finished.

So I'll address those and then tell you an enterframe is the best solution :)

You don't need a graphical representation, or access to the stage to use a enter frame event listener. You can simply do the following:

var s:Shape = new Shape();
s.addEventListener(Event.ENTER_FRAME, caller)

private function caller():void
{
 //do stuff
}

Above we simple create a shape to listen for the enter frame events, and thats all we use it for.

As for the second part, when code is being interpreted at runtime and it comes to a function, caller in this case, it won't execute another function, or line of code outside that function, until it has finished it. So you know that it will never execute again until it has finished the previous call.

So an enterframe (or a timer) are your best / only solutions.

Tyler Egeto
+1  A: 

You might want to experiment with flash.utils.setTimeout(). Put it at the bottom of caller() and have it set a timeout for itself. If you give it a very small timeout interval, it will asynchronously recurse the next time Flash gets the chance.

Alternatively, an ENTER_FRAME event will do more or less the same thing (except at extremely high framerates). Flash will delay the rendering of the next frame until all the processing logic on one frame has finished. Furthermore, Flash is single-threaded, so you can be guaranteed that two copies of your function will never run simultaneously.

zenazn
A: 

What you want to do is dispatch a new event when the Caller() finishes that then calls caller again.

But your need to have a max loop counter otherwise you will just get a stack overflow error.

Don't forget to use weak reference to your event listeners, as it will alway unused object to get garbage collected and help your app run smoother + faster.

package {

    import flash.display.Sprite;
    import flash.events.Event;

    public class Main extends Sprite {

        public function Main() {

            addEventListener("Call_ME_AGAIN", callCaller, false, 0, true );


            caller();            

        }


        private var _counter:int = 0;
        private const LOOP_TIMES:int = 100;
        public function caller(e:Event = null):void {

            trace("hello!");

            if (counter != LOOP_TIMES)
            {
              dispatchEvent(new Event("Call_ME_AGAIN"));
              counter++;
            }
            else if (counter == LOOP_TIMES)
            { //reset the counter so it can happen again when you want
              counter = 0;
             }

        }

        public function callCaller(e:Event = null):void {

            e.stopImmediatePropagation();

            caller(null);

        }

    }

}
Jon
+1  A: 

I've got questions similar to some of the other responders. How often do you want the call to happen? If what you want is for the call to immediately repeat as soon as it finishes, no other part of your program will ever get a chance to execute.

JMHNilbog
A: 

Thank you for your responses. I used Timer and still could overflow with too many calls and too short a timer interval. So I simplified and tried to just make an Event based for loop class (A class that operates like a for loop, but with events to avoid gobbling up all the resources) The solution was to call the function, on it's completion call the timer; on the timer's completion call the function again and bounce them off of each other. Basically: call function wait call function wait etc.

Even if the timer is set to 0 and it freezes the swf until the all the functions are called, the function will complete before running again.

try it out:

package {

import flash.display.Sprite;

public class Efl extends Sprite { // the main class

 public function Efl() {

  // make four functions...
  function init (o:Object):void { // akin to the first part of the for loop

   o.value = 0;

  }

  function condition(o:Object):Boolean { // like the condition portion of the for loop

   if (o.value <= 100) {

    return (true);

   } else {

    return (false);

   }

  }

  function next(o:Object):void { // the increment part of a for loop

   o.value++;

  }

  function statements(o:Object):void { // the body of the for loop

   trace(o.value);

  }

  // put the four functions in one new EventForLoop
  var test1:EventForLoop = new EventForLoop(init, condition, next, statements, 1); // delay is 1 ms
  test1.start(); // set it into motion

  // do it again all in one line - not pretty but it works
  var test2:EventForLoop = new EventForLoop(
   function (o:Object):void { o.value = 0; },
   function (o:Object):Boolean { if (o.value <= 50) return (true); else return (false); },
   function (o:Object):void { o.value++ },
   function (o:Object):void { trace("> " + o.value) },
   20); // delay in 100ms

  test2.start(); // start it up

  // if you try this out, the two will run intertwined since the delays are different.

 }

}

}

Here is the Class that runs the loop:

package {

import flash.events.EventDispatcher;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;

public class EventForLoop extends EventDispatcher {

 // functions to call when simulating the for loop
 private var initializer:Function; // is run once at the start of the loop
 private var condition:Function; // returns boolean to tell the loop to continue or not
 private var step:Function; // the function that runs after the loop is complete
 private var statements:Function; // the actual body of the loop

 private var timeout:Timer; // the timer to avaoid overflows

 private var operator:Object = new Object(); // this is an object to hold and pass values across all the sub loop functions. it is the parameter passed to all four functions

 // some event constants
 static const NEXT:String = new String("EFLNext"); 
 static const DONE:String = new String("EFLDone");


 // constructor just loads vars and sets up timer
 public function EventForLoop (init:Function, cond:Function, stepper:Function, stat:Function, delay:Number = 0) {

  initializer = init;
  condition = cond;
  step = stepper;
  statements = stat;

  timeout = new Timer(delay, 1);

 }

 // the mail loop function...
 private function next(e:Event = null):void {

  // Try this and the lone afte the loop:
  // trace ("start statements");

  if (condition.call(null, operator)) { // if the condition is still true...

   statements.call(null, operator); // do the body statements of the loop
   step.call(null, operator); // increment
   dispatchEvent(new Event(EventForLoop.NEXT)); // dispatch the event so that thw wait can start

  } else { // condition returns false??

   dispatchEvent(new Event(EventForLoop.DONE)); // tell the event dispatcher the loop is done
   removeEventListener(EventForLoop.NEXT, wait); // stop event listeners
   timeout.removeEventListener(TimerEvent.TIMER_COMPLETE, next); 

  }

  // trace ("finish statements\n");
  // this line and the one before the if() will show that the functcion ends before starting again, even if the Timer wait 0ms

 }

 // very simple function that waits and ten triggers the  loop again
 private function wait(e:Event):void {

  timeout.reset();
  timeout.start();

 }

 // metod used to set the loop running
 public function start():void {

  initializer.call(null, operator); // use the initioalizer to set the operator Object
  addEventListener(EventForLoop.NEXT, wait); // when the loops done, wait
  timeout.addEventListener(TimerEvent.TIMER_COMPLETE, next); // when done waiting, loop again

  next(); //do the first loop

 }

}

}

Cheers