0

如果没有 for 循环,我无法让某些事情一遍又一遍地发生。看看这个:

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"));

        }

    }

}

唱这首歌会给你一个“错误#2094:事件调度递归溢出”。真的很快。它将显示事件调度程序和 caller() 在它们自身内部被调用,嵌套直到错误发生。

我想要做的是:“当 caller() 完成时,再次调用它”不是:“在它完成之前调用 caller()”

现在,在人们开始建议使用计时器来猜测需要多长时间或使用 ENTER_FRAME 之前,此 caller() 将没有任何图形数据,也不会连接到 Sprite,完成所需的时间可能与打电话来打电话。我真的在寻找一种只有在它完全完成后才能运行它的方法。

谢谢你的帮助。


谢谢你的回复。我使用了 Timer,但仍然可能因调用过多和定时器间隔太短而溢出。所以我简化并尝试只创建一个基于事件的for循环类(一个像for循环一样运行的类,但带有事件以避免吞噬所有资源)解决方案是调用该函数,在它完成时调用计时器;在计时器完成时再次调用该函数并将它们相互反弹。基本上:

call function
wait
call function
wait etc.

即使计时器设置为 0 并冻结 swf 直到调用所有函数,该函数也会在再次运行之前完成。

试试看:

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.

    }

} 
}

这是运行循环的类:

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

    }

} 

}
4

6 回答 6

1

您可能想尝试使用flash.utils.setTimeout(). 把它放在底部caller()并让它为自己设置一个超时。如果你给它一个非常小的超时间隔,它会在下次 Flash 有机会时异步递归。

或者,一个 ENTER_FRAME 事件将或多或少地做同样的事情(除了极高的帧率)。Flash 将延迟下一帧的渲染,直到一帧上的所有处理逻辑完成。此外,Flash 是单线程的,因此可以保证函数的两个副本永远不会同时运行。

于 2009-07-25T17:27:52.763 回答
1

我有与其他一些响应者类似的问题。您希望电话多久发生一次?如果您想要的是在调用完成后立即重复调用,那么程序的其他部分将永远不会有机会执行。

于 2009-07-25T22:54:27.630 回答
0

这是作业吗?

如果您不想要 for 循环,那么 while 循环怎么样?

尝试使用计时器可以工作,但它会变得混乱。如果您绝对必须使用 Timer,那么如果您的函数仍在运行,则将一些布尔标志设置为 true/false。计时器事件将查看您的函数是否已完成,如果已完成,则再次调用它。

于 2009-07-25T06:37:17.817 回答
0

我会使用 enterFrame...Flash 是基于帧的...当您的过程完成后,您检查是否还有时间再次调用该函数,如果没有,请等待下一帧的到来...

addEventListener("enterFrame", loop);
function loop(e) {
   var maxtime=1000/stage.frameRate;
   var t1=getTimer();
   while(getTimer()-t1 < maxtime) {
      myProcess();
   }
}
于 2009-07-25T12:31:56.973 回答
0

您要做的是在 Caller() 完成时分派一个新事件,然后再次调用 caller。

但是你需要有一个最大循环计数器,否则你只会得到一个堆栈溢出错误。

不要忘记对事件侦听器使用弱引用,因为它总是会收集未使用的对象并帮助您的应用程序运行更流畅+更快。

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);

        }

    }

}
于 2009-07-25T17:28:36.740 回答
0

好的,我知道你说

他的 caller() 不会有任何图形数据,也不会连接到 Sprite

我真的在寻找一种只有在它完全完成后才能运行它的方法。

所以我会解决这些问题,然后告诉你一个enterframe是最好的解决方案:)

您不需要图形表示或访问舞台即可使用进入帧事件侦听器。您可以简单地执行以下操作:

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

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

上面我们简单地创建了一个形状来监听输入帧事件,这就是我们使用它的全部。

至于第二部分,当代码在运行时被解释并且涉及到一个函数时,在这种情况下是调用者,它不会执行另一个函数或该函数之外的代码行,直到它完成它。所以你知道它在完成上一次调用之前永远不会再次执行。

因此,enterframe(或计时器)是您最好/唯一的解决方案。

于 2009-07-25T16:50:34.323 回答