博客首页|TW首页| 同事录|业界社区
2011-04-05

因为工作需要,最近也开始学习和使用 pureMVC , 之前也写过一个 pureMVC Hello World教程, 算是开始入门这个框架和AS这种语言.经过两周多的学习和使用,生出了几个疑问, 和公司的做AS的同事 沟通时,发觉他也并不能清楚地说出所以然来,所以决定自行研究这些问题,并尝试给出解答.

问题大致有下面几个:

  1. 如果一个notification有多个订阅者,那么这多个订阅者之前的执行顺序是怎么样的?
  2. 一段代码在执行时,如果发出一个notification,是等待相应的notification订阅者处理结束再继续呢,还是 直接继续,相应的notification订阅者异步地执行?
  3. 一个notification的多个订阅者之间的执行是异步的,还是顺序执行的?
  4. 是否可能出现notification死循环的问题? (即notification A的订阅者X发出notification B,B的订阅者 又发出notification A,形成一个没有出口的循环)

研究和解决上述问题

为了准确研究出上述的问题和答案,我采用的方法包括两种: 实验和源代码阅读(其实只要阅读源代码即可).

实验

相关的代码可从 TestPureMVC 下载.

这个程序主要是如下的流程:

Startup -> StartUpCommand -> registerMediator(AMediator, BMediator, CMediator) -> sendNotification(Test) -> CMediator(Test) -> sendNotification(Second) ->CMediator(Second) ->sendNotification(Test)

从而形成了一个循环,另外,也可简单地在StartUpCommand类中更改来测试其它的情况.

从代码中能够说明(对应于本文最初的4个问题):

  1. notification的多个订阅者的执行顺序是按照其注册的顺序执行的,也就是全局的view中维护的mediator数组中的顺序
  2. 因为AS中没有类似于sleep的方法,所以无法确定2(具体结果参考下文,代码分析部分)
  3. 同2
  4. 会出现死循环,最终会出现栈溢出的错误

源代码分析

因为Command和Mediator都可以处理和发送notification,所以这里只以Mediator的处理为例来说明.

我们先看notification是如何通知的:

// org.puremvc.as3.core.Views.as
public function notifyObservers( notification:INotification ) : void
{
    if( observerMap[ notification.getName() ] != null ) {

        // Get a reference to the observers list for this notification name
        // 获得这个notification的所有订阅者数组
        var observers_ref:Array = observerMap[ notification.getName() ] as Array;

        // Copy observers from reference array to working array,
        // since the reference array may change during the notification loop
        //  notification的循环中可能增加新的订阅者,所以这里深度拷贝一份
        // 注意顺序并没有更改
        var observers:Array = new Array();
        var observer:IObserver;
        for (var i:Number = 0; i < observers_ref.length; i++) {
            observer = observers_ref[ i ] as IObserver;
            observers.push( observer );
        }

        // Notify Observers from the working array
        // 根据数组中的顺序依次来通知相应的订阅者
        // 注意这里是顺序执行的
        for (i = 0; i < observers.length; i++) {
            observer = observers[ i ] as IObserver;
            observer.notifyObserver( notification );
        }
    }
}

接着来看事件是如何注册的,从而形成订阅者的数组:

// org.puremvc.as3.core.Views.as
public function registerMediator( mediator:IMediator ) : void
{
    // do not allow re-registration (you must to removeMediator fist)
    if ( mediatorMap[ mediator.getMediatorName() ] != null ) return;

    // Register the Mediator for retrieval by name
    mediatorMap[ mediator.getMediatorName() ] = mediator;

    // Get Notification interests, if any.
    var interests:Array = mediator.listNotificationInterests();

    // Register Mediator as an observer for each of its notification interests
    if ( interests.length > 0 )
    {
        // Create Observer referencing this mediator's handlNotification method
        var observer:Observer = new Observer( mediator.handleNotification, mediator );

        // Register Mediator as Observer for its list of Notification interests
        for ( var i:Number=0;  i<interests.length; i++ ) {
            // 注意: 这里是根据Mediator有兴趣的notification来分别加入到对应的订阅者数组中
            // 而加入的次序正好是根据这个Mediator的注册顺序
            registerObserver( interests[i],  observer );
        }
    }

    // alert the mediator that it has been registered
    mediator.onRegister();

}

// registerObserver的具体实现
public function registerObserver ( notificationName:String, observer:IObserver ) : void
{
    var observers:Array = observerMap[ notificationName ];
    if( observers ) {
        // 顺序加入到对应的订阅者数组中
        observers.push( observer );
    } else {
        observerMap[ notificationName ] = [ observer ];
    }
}

我们再看通知给订阅者时的处理逻辑:

public function notifyObserver( notification:INotification ):void
{
    // 根据notification传过来的上下文来执行相应的处理逻辑
    this.getNotifyMethod().apply(this.getNotifyContext(),[notification]);
}

到此,从我们对源代码的分析上,我们就可以完整的回答本文初始的4个问题:

  1. 如果一个notification有多个订阅者,那么多个订阅者之间是按照注册的顺序来执行的
  2. 在AS中不存在异步的执行,所以,当前的代码的执行会等待所有的notification按照1中 的顺序执行完成后,才开始继续执行当前的代码(相当于调用一个函数)
  3. 一个notification的多个订阅者之间是顺序执行的,顺序是按照1中的说明
  4. 存在死循环的可能,因为如2中说明,sendNotification相当于根据注册的顺序来顺序执行 相应的处理逻辑,如果在处理逻辑中又包含触发sendNotification的事件,则整个执行 会成为一个死循环,从而导致栈溢出

总结

通过对本文开始4个问题的分析,弄清楚了 pureMVC 核心的notification机制的几个 核心问题,对于后续的工作和学习都是很有益处的.

每日的生活大致分为三个部分即: 锻炼, 阅读和工作。

锻炼我也想分为两部分, 一部分是室外的锻炼,一个是室内的锻炼。 前者的最佳时间 也许不是早上 但是早晨通常是比较容易坚持的,所以锻炼的时间我还是选择在 早餐前的时间。 室内的是在休息前的锻炼,如做几十个仰卧起坐。这样即使不能减肥, 应该也能保持体重。

阅读,无论是阅读电子书,或者纸质书,通常都能让人平静下心来,不至为社会的浮躁 而累,看书时内心通常是比较安静地,能够与作者一起徜徉在思想,故事,行走之中, 聆听作者反思或者研究后的著述,总之这个过程都是你在静静地聆听,正如我们日常的 聆听一样,聆听总是能够让人安静,反思与更加有耐心的。 我的阅读时间分为两段,一段是技术类的阅读, 这想这个大致可放在晚上晚餐后的时间, 另一段是非技术类的阅读,如文学,哲学,管理等方面,这个大致可在午餐后的时间完成。 两段阅读时间正好作为上,下午两段工作时间的调剂。

再就是工作, 每日大多数的时间会隶属于这一类, 而编程相对比较耗费精力,容易累, 所以需要调剂好整个工作计划和执行过程。 事无巨细, 总是要按照优先级来确定执行的 顺序,而今天要做的事情,通常在昨天已经确定,至少80%已经确定。 数量不要太多, 以能够完整地完成为最重要的目标,这样的循环也能让人更加自信于这样的工作流程。

整个的工作计划与执行,我将会采用ticket的方式来管理,基本的流程如下:

  1. 第一天晚上根据当天ticket的完成情况来制定第二天的工作计划与安排
  2. 第二根据上面的工作计划来执行
  3. 第二天晚上对当天的工作进行总结
  4. 第二天晚上临下班前制定出第三天的工作计划与安排,回到1

总之,需要达到的一个目标是, 能够比较好地完成既定的工作计划与安排, 不把工作 带回家(将生活和工作能够有效区分), 不至于太过受到社会浮躁的影响,能够较好地 保持一个平静的心。

具体的安排为:

关于上图的注解:

  1. 时间结点都是事件结束的时间
  2. 上述的生活和工作安排是针对工作日
  3. 休息日的流程大致会与这个图表相同,只是会在工作量和工作内容的安排上有所调整

检验

无法检验结果的计划根本就称不上计划,那么我的这个工作计划如何来检验呢? 我想主要 还是通过两个方面:

  1. 自检(这是最主要的检验方式,确认自己每日计划的执行情况)
  2. 他检(希望老婆也能够监督)