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

  • このエントリーをはてなブックマークに追加
  • 80

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

■やろうとしたこと

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

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

ActionScript

  1. addCommand(
  2.     new ForLoop(10,
  3.         new SerialList(
  4.             new Trace("コマンド1個目"),
  5.             new Trace("コマンド2個目")
  6.         )
  7.     )
  8. );

すると、どうなるか。

理想
コマンド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()関数のコードを示す。

ActionScript

  1. public function execute( extra:Object = null ):void {
  2.     // 無効化されていたら終了する
  3.     if ( !_enabled ) { return; }
  4.    
  5.     // 実行中なら終了する
  6.     if ( _running ) { return; }
  7.    
  8.     _extra = extra || {};
  9.    
  10.     // 処理を開始する
  11.     _running = true;
  12. ・・・

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

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

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

ActionScript

  1. protected function _executeComplete():void {
  2.     // 事後処理を実行する
  3. ・・・
  4.  
  5.     // 処理を終了する
  6.     _running = false;
  7. ・・・

このように、事後処理(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()の中身はこう。

ActionScript

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

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

ActionScript

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

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

つまり、

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

ということだ。

■あとがき

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

スポンサーリンク
スポンサーリンク
  • このエントリーをはてなブックマークに追加

フォローする

スポンサーリンク
スポンサーリンク