Как упорядочить события в Actionscript 3 - PullRequest
3 голосов
/ 10 февраля 2010

В последние недели я много работал с AS3 и столкнулся с ситуацией, с которой Google не смог помочь. Суть проблемы заключается в том, что перед запуском функции мне нужно знать, что определенные условия были выполнены, например, загрузить мой config.xml и поместить его в переменную класса. Я сталкиваюсь с проблемами в том, что URLLoader является асинхронным, поэтому я не могу поместить код после загрузки, единственное, что я могу сделать, это поместить его в лямбду на слушателе.

var conf:Object = new Object();

var ldr:URLLoader = new URLLoader();
ldr.addEventListener(Event.COMPLETE, geoLoader, false, 0, true);
ldr.load(new URLRequest("http://ipinfodb.com/ip_query.php"));

function geoLoader (e:Event):void
{
    var data = new XML(e.target.data);

    conf.zip    = data.ZipPostalCode.toString();
    conf.city   = data.City.toString();
    conf.state  = data.RegionName.toString();
}

Или немного более кратко, и я считаю, что более понятным:

var conf:Object = new Object();

var ldr:URLLoader = new URLLoader();
ldr.addEventListener(Event.COMPLETE, function (e:Event){
    var data = new XML(e.target.data);

    conf.zip    = data.ZipPostalCode.toString();
    conf.city   = data.City.toString();
    conf.state  = data.RegionName.toString();
}, false, 0, true);
ldr.load(new URLRequest("http://ipinfodb.com/ip_query.php"));

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

Итак, мой вопрос: как профессионалы в области флэш-памяти справляются с предварительными условиями? Я пытаюсь найти решение, которое допускает множество предварительных условий, а также решение, которое допускает различные порядки предварительных условий или только несколько предварительных условий от группы.

Редактировать: Я не уверен, что изначально ясно дал понять, но проблема в том, что у меня будет 5 предварительных условий, что означает, что я получу 5 из указанных выше блоков. Как вы можете себе представить, наличие 5 вложенных слушателей создает ужасный код для спагетти. Например, одна из моих функций должна знать: когда загружается файл config.xml, когда обновляется местоположение, когда загружаются текущие погодные условия и когда загружаются изображения для текущей погоды. Однако есть другая функция, которая должна знать только при загрузке config.xml и обновлении местоположения. Так что это действительно сложный набор, но я уверен, что я не единственный, кто имел дело со сложным набором событий.

Также стоит отметить, что я посмотрел на Seocular's Sequence.as, но, насколько я понимаю, он основан на результатах, а не на событиях. Но я могу ошибаться по этому поводу.

Спасибо за любую помощь, которую вы можете предложить!

Ответы [ 3 ]

3 голосов
/ 10 февраля 2010

Я использую комбинацию библиотек с открытым исходным кодом и пользовательских классов для обработки именно этого типа ситуации.

Для загрузки я в основном использую BulkLoader , поскольку он будет обрабатывать все, что вы хотите загрузить, и все события готовы для прослушивания.

Главное, к чему я стремлюсь, - это максимально отделить мою реализацию. По этой причине любая функция, которая опирается на данные, обрабатывается посредством подписки, настройки уведомлений. Я использую пользовательский класс, который я называю DataController. DataController - это одноэлементный класс, который имеет несколько основных функций, включая:

subscribe, notify

Допустим, у меня есть ситуация, когда я хочу загрузить кучу изображений, но только тогда, когда мой XML, содержащий пути к изображениям, полностью загружен Я бы сделал это так:

Внутри моего класса документов или какого-то другого класса типов контроллеров я бы использовал BulkLoader для загрузки некоторого XML. В полном обработчике я бы взял XML, скажем imageXML и выполнил команду, подобную этой:

DataController.instance.notify("ImageXMLLoaded", imageXML);

В моем классе ImageHolder у меня было бы следующее:

public function ImageHolder() {
    DataController.instance.subscribe("ImageXMLLoaded", _populate);
}

private function _populate($imageXML:XML):void {
    // do whaterver with your XML
}

DataController имеет несколько переменных экземпляра, подобных этим:

private var _subscribers:Dictionary = new Dictionary(true);
private var _processed:Dictionary = new Dictionary(true);
private var _requests:Dictionary = new Dictionary(true);

Функция подписки выглядит примерно так:

public function subscribe($eventName:String, $subscriber:Function): void {
    var _funcArray:Array;
    var _func:Function;
    if (_processed[$eventName]) {
        $subscriber(_processed[$eventName]);
        return;
     }
     if (! _subscribers[$eventName]) {
        _subscribers[$eventName] = new Array();
     }
     _subscribers[$eventName].push($subscriber);
}        

И функция уведомления выглядит так:

public function notify($eventName:String, $data:*, $args:Object = null): void {
    var _cnt:Number;
    var _subscriberArray:Array;
    var _func:Function;

   _processed[$eventName] = $data;

   if (_subscribers[$eventName] != undefined) {
       _subscriberArray = _subscribers[$eventName].slice();
       _cnt = 0;
       while (_cnt < _subscriberArray.length) {
           _func = _subscriberArray[_cnt] as Function;

           if ($args) {
               _func($feedXML, $args);
           } else {
               _func($feedXML);
           }
           _cnt++;
        }
    }
    if (_requests[$eventName]) {
        _subscriberArray = _requests[$eventName].slice();
        delete _requests[$eventName];
        _cnt = 0;

        while (_cnt < _subscriberArray.length) {
        if (_subscriberArray[_cnt] != null) {
                _subscriberArray[_cnt]($data);
            }
            _cnt++;
        }
    }
}

И моя функция запроса:

public function request($eventName:String, $requestFunction:Function): void {
    var _requestArray:Array;
    var _request:Function;

    if (! _feeds[$eventName]) {
        if (! _requests[$eventName]) {
            _requests[$eventName] = new Array();
        }
        _requests[$eventName].push($feedFunction);
    } else {
        $requestFunction(_feeds[$eventName]);
    }
}

Обычно, когда вы вызываете подписку, он сначала проверяет, были ли уже установлены данные $ eventName в _processed. Если это так, он передает эти данные в свою функцию подписки (в данном случае _populate.) Если она не была загружена, он добавит функцию подписки в словарь _subscribeers и больше ничего не будет делать. Когда данные отправляются с помощью уведомления, они сначала устанавливаются в _processed Dictionary, затем DataController зацикливается и подписывается на $ eventName и передает данные каждой из функций подписки.

Есть также методы, которые я пропустил, которые позволяют вам делать то же самое, но без фактической передачи каких-либо данных. Они полезны для анимации событий или для сцепления любых других типов событий.

У меня также есть методы для удаления подписчиков и удаления данных из _processed Dictionary.

Надеюсь, все это имеет смысл.

EDIT

Исходя из ваших правок, вы можете сделать что-то вроде этого:

Код где-то, может быть, в вашем классе документов.

public function startMyLoading():void {
    loadConfig(onConfigLoaded);
}

public function onConfigLoaded($xml:XML):void {
    DataController.instance.notify("configLoaded", $xml);
    loadWeatherContiditons(onWeatherConditionsLoaded);
}

public function onWeatherConditionsLoaded($xml:XML):void {
    DataController.instance.notify("weatherXMLLoaded", $xml);
}

Код где-то, может быть, какой-то вид отображения погоды:

public function WeatherDisplayView():void {
    DataController.instance.subscribe("weatherXMLLoaded", _populateWeatherPanel);
}

private function _populateWeatherPanel($xml:XML):void {
    // for each (var weatherNode:XML in $xml.children()) ...
    // use BulkLoader to load, and add Event.COMPLETE function for when it is finished.
}

private function _onBulkLoaderComplete($evt:Event):void {
    DataController.instance.broadcast("ImagesComplete"); // the broadcast function is one that I did not include above, but basically works the same as subscribe, notify, but without any data being passed.
}

Код в чем-то, требующем загрузки только config.xml:

public function SomeClass():void {
    DataController.instance.subscribe("configLoaded", _handleConfigLoaded);
}

private function _handleConfigLoaded($xml:XML):void {
    // do something with config
}

При использовании этого метода ваши запросы фактически синхронны. После загрузки config.xml загружается weather.xml. После этого он загружает что-то еще ... и т. Д. И т. Д.

Вы также можете загрузить свои 5 вещей с помощью BulkLoader и прослушать каждую из них для завершения. Запуская уведомления и трансляции по мере необходимости.

1 голос
/ 10 февраля 2010

Может быть, это ситуация для опроса? Создайте «условные выражения», в которых есть прослушиватель enterframe, который вызывает их метод isSatisfied () ovverridden, в который вы помещаете любую логику, которая сообщит вам, пора ли выполнять execute ()

public function onEnterFrame(event:Event):void
{
   if(isSatisfied())
   {
     execute();
     removeEventListener(Event.ENTER_FRAME,onEnterFrame);
   } 
}
1 голос
/ 10 февраля 2010

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

Вы можете использовать 2 вещи:

  1. Обработчик Event.COMPLETE, который сообщает, когда данные готовы
  2. ваш объект conf.

Я бы на самом деле объявил conf, но я бы не стал его инициализировать. Я бы инициализировал его, когда данные будут готовы. В любом случае это не поможет заранее. Тем не менее, это позволило бы мне проверить, есть ли данные ... Если conf не равен null, то я мог бы использовать некоторые данные, в противном случае, если данные не были загружены, запустите загрузку и т. Д.

НТН, George

...