Один за другим

У меня возникают проблемы с тем, чтобы что-то происходило снова и снова без цикла 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"));

        }

    }

}

sing это приведет к ошибке «Ошибка № 2094: переполнение рекурсии отправки событий». действительно быстро. Это покажет, что диспетчер событий и caller() вызываются внутри самих себя, вложенные друг в друга, пока не произойдет ошибка.

Что я хочу сделать, так это: «Когда caller () будет выполнен, вызовите его снова», а не: «вызовите caller () до его завершения»

Теперь, прежде чем люди начнут предлагать использовать таймер, чтобы угадать, сколько времени это займет, или использовать ENTER_FRAME, этот caller() не будет иметь никаких графических данных и не будет подключен к спрайту, и время, необходимое для завершения, может сильно отличаться от звонить звонить. Я действительно ищу способ запустить его только после того, как он полностью завершится.

Спасибо за вашу помощь.


Спасибо за ваши ответы. Я использовал таймер и все еще мог переполниться слишком большим количеством вызовов и слишком коротким интервалом таймера. Поэтому я упростил и попытался просто создать класс Event на основе цикла 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

    }

} 

}

person Community    schedule 25.07.2009    source источник


Ответы (6)


Вы можете поэкспериментировать с flash.utils.setTimeout(). Поместите его в конец caller() и установите для себя тайм-аут. Если вы дадите ему очень маленький интервал времени ожидания, он будет асинхронно повторяться в следующий раз, когда Flash получит шанс.

В качестве альтернативы, событие ENTER_FRAME будет делать примерно то же самое (за исключением чрезвычайно высокой частоты кадров). Flash задерживает рендеринг следующего кадра до тех пор, пока не завершится вся логика обработки одного кадра. Кроме того, Flash является однопоточным, поэтому вы можете быть уверены, что две копии вашей функции никогда не будут выполняться одновременно.

person zenazn    schedule 25.07.2009

У меня есть вопросы, похожие на некоторые из других ответивших. Как часто вы хотите, чтобы звонок происходил? Если вы хотите это, чтобы вызов немедленно повторился, как только он завершится, никакая другая часть вашей программы никогда не получит шанса на выполнение.

person JMHNilbog    schedule 25.07.2009

Это за задание?

Если вам не нужны циклы for, как насчет цикла while?

Попытка использовать таймеры может сработать, но это становится грязным. Если вам абсолютно необходимо использовать таймер, установите логический флаг в значение true/false, если ваша функция все еще работает. Событие таймера увидит, завершена ли ваша функция, и если да, то вызовите ее снова.

person Jake    schedule 25.07.2009

Я бы использовал enterFrame... Flash основан на кадрах... когда ваш процесс завершен, вы проверяете, есть ли у вас еще время для другого вызова функции, если нет, просто подождите, пока придет следующий кадр...

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

Хорошо, я знаю, ты сказал

его caller() не будет иметь никаких графических данных и не будет подключен к спрайту

А также

Я действительно ищу способ запустить его только после того, как он полностью завершится.

Поэтому я рассмотрю их, а затем скажу, что энтерфрейм — лучшее решение :)

Вам не нужно графическое представление или доступ к сцене, чтобы использовать прослушиватель событий ввода кадра. Вы можете просто сделать следующее:

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

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

Выше мы просто создаем форму для прослушивания событий ввода кадра, и это все, для чего мы ее используем.

Что касается второй части, когда код интерпретируется во время выполнения и приходит к функции, вызывающей в данном случае, он не будет выполнять другую функцию или строку кода вне этой функции, пока не завершит ее. Таким образом, вы знаете, что он никогда не будет выполняться снова, пока не завершит предыдущий вызов.

Таким образом, энтерфрейм (или таймер) - ваши лучшие / единственные решения.

person Tyler Egeto    schedule 25.07.2009

Что вы хотите сделать, так это отправить новое событие, когда 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);

        }

    }

}
person Jon    schedule 25.07.2009