Progression Frameworkで、ForLoopコマンドでSerialListを実行させたら死亡事件
例によってProgression Frameworkの話。今日はForLoopコマンドとSerialListを使って、複数のコマンドを連続実行させようかと企んだが、それはProgression Frameworkの仕組み的に無理だったと言う事がわかって終わったって話。
■やろうとしたこと
ForLoopって言う、コマンドを繰り返し実行してくれるコマンドがあったので、早速使おうと。
で、1個だけコマンド実行されてもあんまり意味ないし、SerialList使ってつなげようと、そう言う訳です。
こんな感じ。勿論、実際に使う時はもっと有益なコマンドを使いますが。
new ForLoop(10,
new SerialList(
new Trace("コマンド1個目"),
new Trace("コマンド2個目")
)
)
);
すると、どうなるか。
- 理想
-
コマンド1個目 コマンド2個目 コマンド1個目 コマンド2個目 … コマンド1個目 コマンド2個目
で10回繰り返し
- 現実
-
コマンド1個目 コマンド2個目
あれ?1回で終わってる??
- ForLoop
- SerialList
- CommandList(SerialListの親)
- Command(CommandListの親)
execute();_executeStart();_executeProgress();_executeComplete();- まず
execute()の頭で、_runningの値をチェックし、trueであれば現在コマンドが実行中であると考えて実行を中止する。 - 処理の実行に先立ち、これからコマンドが実行中になると言う意味で
_runningにtrueを代入。 execute();_executeStart();_executeProgress();_execute();リスト内の1つ目のCommandのexecute();リスト内の2つ目のCommandのexecute();リスト内のN個目のCommandのexecute();
・・・
_executeFinally();_executeFinallyComplete();
■なぜ、ループは1回しか回らないのか?
というわけで、ループする予定が1周で終了してしまった。何故だろうか?どこかに原因がある筈。
この事件の犯人は、一体誰なのか・・・?
実は、原因はCommandとCommandListの、終了時の処理の違いにあった。
まず、以下にCommandの実行時に呼ばれる関数を示す。
こんな感じ。この順序で処理が進んでいく。メインの処理は_executeProgress()内に記述されてる。
で、次に最初に呼ばれる、execute()関数のコードを示す。
// 無効化されていたら終了する
if ( !_enabled ) { return; }
// 実行中なら終了する
if ( _running ) { return; }
_extra = extra || {};
// 処理を開始する
_running = true;
・・・
今回の件で重要なのは、_runningの値。なので、そこに注目する。
見ての通り、次のような処理をしている。
で、コマンドの処理が実行され、いよいよ終了だ!となった時。
通常のCommandであれば、_executeComplete()関数が呼ばれ、
// 事後処理を実行する
・・・
// 処理を終了する
_running = false;
・・・
このように、事後処理(afterで設定した関数を呼ぶ)が行われ、その後_runningをfalseにしている。これによって、次にまたexecute()を呼んでも、コマンドを実行することが出来る。
一方SerialListの場合、関数はこんな感じで呼び出される
で、最後の関数_executeFinallyComplete()の中身はこう。
// 事後処理を実行する
if ( _afterFunc is Function ) {
_afterFunc.apply( _afterScorp, _afterArgs );
}
// イベントを送出する
dispatchEvent( e );
}
そう、問題はここである。
大切な大切な、「_runningをfalseにする」処理がどこにも無い。なので、SerialListは、一旦コマンドが開始されたら永久に「俺今仕事中だから、新しい仕事は後にしてもらえます?」状態に突入してしまうのだ。ということで、ForLoopコマンドが2回目のSerialListを実行しようとすると、
if ( _running ) { return; }
これに引っ掛かって処理が停止してしまう。
しかも悪いことに、この終わり方だとコマンド終了のイベントも送出されないので、ForLoopは以後の処理を一切続けられないと言う危険を孕んでいる(ForLoopは1回1回のコマンドをイベントで管理している)。その上_runningはprivateで、get用のプロパティしかない。なので、この時点で手詰まりなのだ。
つまり、
ForLoopとSerialListは組み合わせるな!
ということだ。
■あとがき
ここまで書いて、
「あ、これフォーラムに投稿した方がよくね?」
と思ったが、長いしもう書いちゃったのでいいや。




