Статический метод для класса XMLLoader ... как вернуть данные XML после функции Event.COMPLETE? - PullRequest
1 голос
/ 19 июля 2010

Я пытаюсь создать общий класс XMLLoader со статическим методом LOAD с намерением использовать его следующим образом ...

private var _data:XML = XMLLoader.LOAD("path/to/xml_file.xml");

Тогда я мог бы использовать его в любом клиенте и перейти в город с e4x.

Общая проблема, с которой я сталкиваюсь, связана с событием COMPLETE URLLoader, которое обязательно вызывает другую функцию для установки данных XML. Это лишает меня возможности вернуть XML из метода LOAD, поскольку данные задаются вне этой функции. Очевидно, мне нужно дождаться события COMPLETE, чтобы убедиться, что данные доступны.

Хотя, возможно, я мог бы создать и вернуть функцию _waitForData, которая рекурсивно вызывает себя до тех пор, пока не установлена ​​_data, а затем возвращает данные. Но это кажется избыточным (так как Event.COMPLETE делает это в любом случае), и способ, которым я попытался это, генерирует ошибку переполнения стека.

Вот что я пытался:

public class XMLLoader {

    private static var _url:String = "";
    private static var _data:XML = null;

    public static function LOAD(url:String):XML {

        _url = url;
        var _xmlLoader:URLLoader = new URLLoader();
            _xmlLoader.addEventListener(Event.COMPLETE, _setData);
            _xmlLoader.load(new URLRequest(_url));

        return _waitForData();
    }

    static function _setData(e:Event):void {
        XML.ignoreWhitespace = true;
        _data = new XML(e.target.data);
    }

    static function _waitForData():XML {

        if( _data == null ) {
             _waitForData();
        }

        return _data;
    }

    public function LoadXML() {}

}

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

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

Ответы [ 2 ]

4 голосов
/ 19 июля 2010

Вы не хотите идти по полностью статическому маршруту. Это беспорядок, усложняет вещи без необходимости, трудно поддерживать, и что, если вы хотите загрузить два xmls одновременно? (подсказка: вы не можете).

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

Я думаю что-то в этом роде (просто написал и скомпилировал нормально; не проверил это полностью, но этого должно быть достаточно, чтобы дать идею).

package
{
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.SecurityErrorEvent;
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    import flash.utils.Dictionary;

    public class XMLLoader extends EventDispatcher
    {
        private var _loader:URLLoader;
        private var _data:XML;
        private var _callback:Function;

        private static var _map:Dictionary = new Dictionary();

        public function XMLLoader(callback:Function = null)
        {
            _data = null;
            _callback = callback;
            _loader = new URLLoader();
            _loader.addEventListener(IOErrorEvent.IO_ERROR,handleError);
            _loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,handleError);
            _loader.addEventListener(Event.COMPLETE,handleComplete);
        }

        public function load(request:URLRequest):void {
            _loader.load(request);  
        }

        private function dispatchError():void {
            cleanUp();  
            dispatchEvent(new Event(Event.COMPLETE));
            if(_callback is Function) {
                _callback(null);
            }
        }

        private function dispatchSuccess():void {
            cleanUp();  
            dispatchEvent(new Event(Event.COMPLETE));
            if(_callback is Function) {
                _callback(_data);
            }
        }

        private function handleError(e:Event):void {
            dispatchError();
        }

        private function handleComplete(e:Event):void {
            var success:Boolean = false;
            try {
                _data = new XML(e.target.data);
                success = true;
            } catch(err:TypeError) {
                success = false;
            } finally {
                if(success) {
                    dispatchSuccess();
                } else {
                    dispatchError();
                }
            }
        }

        public function get data():XML {
            return _data;
        }

        private function cleanUp():void {
            if(_loader) {
                _loader.removeEventListener(IOErrorEvent.IO_ERROR,handleError);
                _loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,handleError);
                _loader.removeEventListener(Event.COMPLETE,handleComplete);
            }
            if(_map[this]) {
                delete _map[this];
            }
        }

        public static function loadXml(url:String,callback:Function):XMLLoader {
            var loader:XMLLoader = new XMLLoader(callback);
            loader.load(new URLRequest(url));
            //  pin down the instance just to be safe; I've seen loaders being collected...
            _map[loader] = true;
            return loader;
        }

    }
}

Использование:

    private function handleResult(data:XML):void {
        if(data) {
            trace(data);
        } else {
            trace("failed");
        }
    }


    private function test():void {
        XMLLoader.loadXml("your.xml",handleResult);
    }

Кроме того, вы можете переключиться на использование методов экземпляра, если вам нужна большая гибкость:

    private function test():void {
        var loader:XMLLoader = new XMLLoader(handleResult);
        loader.load(new URLRequest("your.xml"));        
    }

Этот пример реализации может использоваться с обратным вызовом или событиями. Для простоты я просто определяю одну функцию обратного вызова; Вы можете определить, была ли успешной операция загрузки, проверив полученные данные; ноль означает, что это не удалось. Кроме того, я отправляю событие Event.COMPLETE, чтобы сообщить о завершении загрузки загрузчика (даже если это не удалось); Опять же, вы можете узнать, провалился ли он или нет, проверив свойство data. Вы можете использовать пользовательские события, если хотите, и / или определить обратный вызов сбоя, но я думаю, что вам нужен как можно более простой интерфейс, поэтому я думаю, что этого должно быть достаточно.

0 голосов
/ 19 июля 2010

Вы слишком много просите о системе, вспышка не была предназначена для этого.Вы не можете (или не должны) вешать всю систему только для того, чтобы ждать одного результата.Для записи во flex / flash необходимо использовать обработчики завершения, потому что в некоторых случаях, например, это просто невозможно обойти.Если бы вы делали это таким образом, кадры никогда не двигались бы вперед, и все в программе останавливалось до тех пор, пока вещь не возвращалась, создавая очень несчастный пользовательский опыт, который ощущался бы как «зависание» или «сбой».

С технической стороны, если вы абсолютно, безусловно, настаивали на этом, то вам (1) придется найти способ поместить задержку в цикл if-null и (2) изменить ее наwhile( _data != null ){ /* wait a while */ } но я действительно не рекомендую идти по этому пути.Точнее, вы должны быть осторожны при вызове функции внутри себя.В вашем случае _waitForData просит _waitForData попросить _waitForData попросить _waitForData ... и т. Д., Пока он не достигнет произвольного предела и не остановится.Это называется бесконечная рекурсия .

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

Обновление:

Решение 2. Напишите класс, который будет обрабатывать всю необходимую загрузку XML.Вы можете сделать это заранее заполнить вещи в начале программы, если вы знаете, что может быть запрошено.Затем, когда вам нужно больше, попросите у класса больше.Затем, если он уже загружен, он может сразу же вернуть его обратно.Если у него его нет, он вернет null, извлечет его в фоновом режиме и вызовет обработчик, когда это будет сделано.Таким образом, код, вызывающий его, может, по крайней мере, немедленно попытаться сделать что-то с XML, но в конце он всегда будет обрабатывать случай, когда он не загружен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...