【问题标题】:How to sequence events in Actionscript 3如何在 Actionscript 3 中对事件进行排序
【发布时间】:2011-01-15 08:39:08
【问题描述】:

在过去的几周里,我一直在使用 AS3,但遇到了谷歌无法提供帮助的情况。问题的症结在于,在我运行一个函数之前,我需要知道某些条件已经满足,例如加载我的 config.xml 并将其放入类变量中。我遇到麻烦的地方是 URLLoader 是异步的,所以我不能在加载后放置代码,我唯一能做的就是把它放在监听器的 lambda 中。

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

这对于一小段代码非常有用,但是我有一堆函数依赖于大量的先决条件(如果我使用第一种方法,这会污染命名空间)或者依赖于一个位的函数stack (这意味着我不能使用第二种方法,即使它对我来说稍微明显一些)。

所以,我的问题是:Flash 专业人士如何处理先决条件?我试图找到一个允许大量先决条件的解决方案,以及一个允许不同顺序的先决条件或只允许一组先决条件的解决方案。

编辑:我不确定我最初是否说清楚了,但问题是我将有 5 个先决条件,这意味着我最终得到了上述块中的 5 个。正如你可以想象的那样,拥有 5 个嵌套侦听器会导致糟糕的意大利面条代码。举个例子,我的一个函数需要知道:什么时候加载 config.xml,什么时候更新位置,什么时候加载当前天气条件,什么时候加载当前天气的图像。但是,还有另一个函数只需要知道何时加载 config.xml 并更新位置。所以这是一个非常复杂的场景,但我确信我不是唯一一个必须处理一系列复杂事件的人。

另外值得注意的是,我查看了 Senocular 的 Sequence.as,但据我所知,这是基于回报而不是事件。但我可能错了。

感谢您提供的任何帮助!

【问题讨论】:

    标签: flash actionscript-3 events asynchronous flash-cs4


    【解决方案1】:

    我结合使用开源库和自定义类来处理这种确切类型的情况。

    对于加载,我主要使用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]);
        }
    }
    

    基本上,当您调用 subscribe 时,它​​会首先检查 $eventName 数据是否已在 _processed 中设置。如果有,它会将该数据传递给它的订阅函数(在这种情况下为 _populate)。如果它没有被加载,它会将订阅函数添加到 _subscribers 字典中,并且什么也不做。当通过 notify 发送数据时,会先在 _processed Dictionary 中设置,然后 DataController 会循环遍历并订阅 $eventName 并将数据传递给每个订阅函数。

    还有一些我省略的方法可以让你做同样的事情,但实际上不传递任何数据。这些对于动画事件或将任何其他类型的事件链接在一起很有用。

    我还有删除订阅者和从 _processed Dictionary 中删除数据的方法。

    我希望这一切都有意义。

    编辑

    根据您的编辑,您可以执行以下操作:

    代码某处,可能在您的文档类中。

    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。之后它会加载其他东西......等等等等。

    您还可以使用 BulkLoader 加载您的 5 件事情,并听取每件事情的完成情况。根据需要触发通知和广播。

    【讨论】:

    • 嗯,我以前从没听说过 BulkLoader……看来我已经创建了类似的东西(虽然不那么复杂,也没有那么灵活)!
    • BulkLoader,FTW!我什至将 BulkLoader 和 LoadingItem 抽象为 BulkLoaderGroup 和 BulkLoaderEntry。这允许我控制单个 LoadingItem 的 COMPLETE 处理程序。
    • 这太棒了,感谢您的意见,这正是我想要的!
    【解决方案2】:

    也许这是轮询的情况?创建具有调用其 ovverridden isSatisfied() 方法的 enterframe 侦听器的“条件”,您可以在其中放置任何逻辑会告诉您是否该执行()的时间

    public function onEnterFrame(event:Event):void
    {
       if(isSatisfied())
       {
         execute();
         removeEventListener(Event.ENTER_FRAME,onEnterFrame);
       } 
    }
    

    【讨论】:

    • 我将不得不对此进行更多研究,但这似乎更接近目标。我喜欢能够拥有那个 if 语句。
    【解决方案3】:

    我通常会在开始时尽可能多地加载,只是因为我很懒,而且我想确保在实际做任何事情之前我已经准备好所需的一切。

    你可以利用两件事:

    1. Event.COMPLETE 处理程序,让您知道数据何时准备就绪
    2. 你的 conf 对象。

    我实际上会声明 conf,但我不会在那里初始化它。当数据准备好时,我会初始化它。反正事先也没有用。这将允许我检查数据是否存在...如果 conf 不为空,那么可能会有一些数据供我使用,否则如果数据未加载,则触发加载等。

    HTH, 乔治

    【讨论】:

    • 将 conf 留空是有意义的。但是主要的问题是我最终会得到六个这样的先决条件,并且代码看起来很糟糕。或者条件可能比 conf 为空要复杂得多,可能是 conf 已满,位置对象已使用 x 属性构建,并且在过去的某个时间已调度了三个事件。所以这将是 5 个嵌套的侦听器,但是侦听器过去可能已经被解雇了,所以我也需要存储。毛茸茸的。
    • 你不能先加载所有数据然后初始化所有其他元素吗? BulkLoader 之类的东西可以派上用场
    猜你喜欢
    • 2011-02-22
    • 1970-01-01
    • 2013-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-31
    • 2015-09-03
    相关资源
    最近更新 更多