Progression Frameworkで、ForLoopコマンドでSerialListを実行させたら死亡事件

このエントリをはてなブックマークに追加このエントリをはてなブックマークに追加このエントリをdel.icio.usに追加このエントリをLivedoor Clipに追加このエントリをLivedoor Clipに追加このエントリをYahoo!ブックマークに追加このエントリをFC2ブックマークに追加このエントリをNifty Clipに追加このエントリをPOOKMARK. Airlinesに追加このエントリをBuzzurl(バザール)に追加このエントリをBuzzurl(バザール)に追加このエントリをChoixに追加このエントリをnewsingに追加このエントリをkwoutに追加
2008年4月18日 金曜日1:13:46

例によってProgression Frameworkの話。今日はForLoopコマンドとSerialListを使って、複数のコマンドを連続実行させようかと企んだが、それはProgression Frameworkの仕組み的に無理だったと言う事がわかって終わったって話。

■やろうとしたこと

ForLoopって言う、コマンドを繰り返し実行してくれるコマンドがあったので、早速使おうと。
で、1個だけコマンド実行されてもあんまり意味ないし、SerialList使ってつなげようと、そう言う訳です。

こんな感じ。勿論、実際に使う時はもっと有益なコマンドを使いますが。

addCommand(
    new ForLoop(10,
        new SerialList(
            new Trace("コマンド1個目"),
            new Trace("コマンド2個目")
        )
    )
);

すると、どうなるか。

理想
コマンド1個目
コマンド2個目
コマンド1個目
コマンド2個目
…
コマンド1個目
コマンド2個目

で10回繰り返し

現実
コマンド1個目
コマンド2個目

あれ?1回で終わってる??

■なぜ、ループは1回しか回らないのか?

というわけで、ループする予定が1周で終了してしまった。何故だろうか?どこかに原因がある筈。
この事件の犯人は、一体誰なのか・・・?

  • ForLoop
  • SerialList
  • CommandList(SerialListの親)
  • Command(CommandListの親)

実は、原因はCommandとCommandListの、終了時の処理の違いにあった。
まず、以下にCommandの実行時に呼ばれる関数を示す。

  1. execute();
  2. _executeStart();
  3. _executeProgress();
  4. _executeComplete();

こんな感じ。この順序で処理が進んでいく。メインの処理は_executeProgress()内に記述されてる。
で、次に最初に呼ばれる、execute()関数のコードを示す。

public function execute( extra:Object = null ):void {
    // 無効化されていたら終了する
    if ( !_enabled ) { return; }
   
    // 実行中なら終了する
    if ( _running ) { return; }
   
    _extra = extra || {};
   
    // 処理を開始する
    _running = true;
・・・

今回の件で重要なのは、_runningの値。なので、そこに注目する。
見ての通り、次のような処理をしている。

  1. まずexecute()の頭で、_runningの値をチェックし、trueであれば現在コマンドが実行中であると考えて実行を中止する。
  2. 処理の実行に先立ち、これからコマンドが実行中になると言う意味で_runningtrueを代入。

で、コマンドの処理が実行され、いよいよ終了だ!となった時。
通常のCommandであれば、_executeComplete()関数が呼ばれ、

protected function _executeComplete():void {
    // 事後処理を実行する
・・・

    // 処理を終了する
    _running = false;
・・・

このように、事後処理(afterで設定した関数を呼ぶ)が行われ、その後_runningfalseにしている。これによって、次にまたexecute()を呼んでも、コマンドを実行することが出来る。

一方SerialListの場合、関数はこんな感じで呼び出される

  1. execute();
  2. _executeStart();
  3. _executeProgress();
  4. _execute();
    1. リスト内の1つ目のCommandのexecute();
    2. リスト内の2つ目のCommandのexecute();
    3. ・・・

    4. リスト内のN個目のCommandのexecute();
  5. _executeFinally();
  6. _executeFinallyComplete();

で、最後の関数_executeFinallyComplete()の中身はこう。

protected function _executeFinallyComplete( e:CommandEvent ):void {
    // 事後処理を実行する
    if ( _afterFunc is Function ) {
        _afterFunc.apply( _afterScorp, _afterArgs );
    }
   
    // イベントを送出する
    dispatchEvent( e );
}

そう、問題はここである。
大切な大切な、「_runningfalseにする」処理がどこにも無い。なので、SerialListは、一旦コマンドが開始されたら永久に「俺今仕事中だから、新しい仕事は後にしてもらえます?」状態に突入してしまうのだ。ということで、ForLoopコマンドが2回目のSerialListを実行しようとすると、

// 実行中なら終了する
if ( _running ) { return; }

これに引っ掛かって処理が停止してしまう。
しかも悪いことに、この終わり方だとコマンド終了のイベントも送出されないので、ForLoopは以後の処理を一切続けられないと言う危険を孕んでいる(ForLoopは1回1回のコマンドをイベントで管理している)。その上_runningはprivateで、get用のプロパティしかない。なので、この時点で手詰まりなのだ。

つまり、

ForLoopとSerialListは組み合わせるな!

ということだ。

■あとがき

ここまで書いて、
「あ、これフォーラムに投稿した方がよくね?」
と思ったが、長いしもう書いちゃったのでいいや。

トラックバック URL :

コメントをどうぞ

HTML convert time: 0.583 sec.