如何在Actionscript 3中按顺序排列事件

3

我最近几周一直在使用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"));

这对于短代码段非常有效,但我有许多函数依赖于大量的前提条件(如果使用第一种方法,这会污染命名空间),或者依赖于堆栈的一部分(即使它对我来说更明显,也意味着我不能使用第二种方法)。因此,我的问题是:Flash专业人员如何处理前提条件?我正在寻找一种解决方案,允许大量的前提条件,以及允许不同顺序的前提条件或仅从一组中获取少量前提条件的解决方案。
编辑:我不确定我最初是否表达清楚,但问题在于我将有5个前提条件,这意味着我最终会得到上述5个块中的5个。正如您所想象的那样,拥有5个嵌套听者会导致可怕的意大利面条式代码。例如,我的一个函数需要知道:当config.xml加载时,当位置更新时,当当前天气条件已经加载时以及当当前天气的图像已经加载时。但是还有另一个函数只需要知道config.xml何时加载和位置何时更新。因此,这是一个非常复杂的集合,但我相信我不是唯一一个必须处理复杂事件集的人。
值得注意的是,我已经查看了Senocular的Sequence.as,但据我所知,它基于返回而不是事件。但我可能对此有所错误。谢谢您提供的任何帮助!
3个回答

3
我使用开源库和自定义类的组合来处理这种情况。
对于加载,我主要使用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
}

数据控制器有一些实例变量,例如这些:
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);
}        

而 notify 函数看起来像这样:

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时,它将首先检查_processed中是否已设置$eventName数据。 如果有,则会将该数据传递给其订阅函数(在此情况下为_populate)。 如果尚未加载,则将订阅函数添加到_subscribers字典中,然后不执行任何其他操作。 当通过notify发送数据时,它将首先在_processed字典中设置数据,然后DataController将循环遍历$eventName的所有订阅者,并将数据传递给每个订阅函数。
还有我省略的方法可以让您做同样的事情,但不实际传递任何数据。 这些对于动画事件或链接任何其他类型的事件非常有用。
我还有删除订阅者和从_processed字典中删除数据的方法。
我希望这一切都说得通。
编辑
根据您的编辑,您可以像这样做:
在某个地方的代码,也许在您的文档类中。
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...看起来我创建了类似的东西(虽然不太复杂,也不太灵活)! - Cameron
BulkLoader,非常好!我甚至将BulkLoader和LoadingItem抽象化为BulkLoaderGroup和BulkLoaderEntry。这使我能够控制单个LoadingItem的COMPLETE处理程序。 - sberry

1
也许这是一个轮询的情况?创建具有enterframe侦听器的“条件”,该侦听器调用其覆盖的isSatisfied()方法,在其中放置任何逻辑,以告诉您是否到了执行时间。
public function onEnterFrame(event:Event):void
{
   if(isSatisfied())
   {
     execute();
     removeEventListener(Event.ENTER_FRAME,onEnterFrame);
   } 
}

我需要更深入地研究一下,但这似乎更接近标准答案。我喜欢能够使用那个if语句。 - Chuck Vose

1

我通常会在开始时尽可能多地加载,只是因为我很懒,想确保在实际操作之前已经准备好了所有需要的东西。

你可以利用两个东西:

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

我会声明conf,但不会在那里初始化。我会在数据准备好时进行初始化。这样做也无济于事。这样做可以让我检查数据是否存在...如果conf不为空,则可能有一些数据可供我使用,否则如果数据未加载,则触发加载等操作。

希望对你有所帮助, George


这样留空 conf 是有道理的。但主要问题是我最终会得到大约六个这样的前提条件,代码看起来很糟糕。或者条件可能比 conf 为空更复杂,可能是像 conf 已满、位置对象已建立 x 属性和过去某个时间点已分派了三个事件之类的东西。那么就需要五个嵌套的侦听器,但侦听器可能已经在过去触发过了,所以我也需要存储。这很棘手。 - Chuck Vose
你能不能先不加载所有数据,然后再初始化其他元素?类似BulkLoader这样的工具可能会派上用场。 - George Profenza

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接