MovieClip.loadMovie や XML.load など、非同期のリクエストを正しい順序で行うには Command パターンとキューを使うと良さそう。……というわけで、簡単な Command クラスと CommandQueue クラスを書いてみた。CommandQueue に Command インスタンスを enqueue で詰め込んでから、dequeue して一連のコマンド (リクエスト) を実行する仕組み。
まずは、Command クラス。インターフェイスにしたいところだけど、EventDispatcher を使う関係で、コンストラクタを private にした抽象クラスにした。実行メソッドを execute としている。
import mx.events.EventDispatcher; /** * コマンド */ class Command { public var addEventListener:Function; private var dispatchEvent:Function; public var execute:Function; /** * コンストラクタ * @param command コマンドオブジェクト */ private function Command(command:Command) { EventDispatcher.initialize(command); } }
次に、上記 Command クラスを継承した HogeCommand クラス。execute が呼び出されるとタイマーが作動して、止まったら onComplete イベントを発行する。
/** * ほげコマンド */ class HogeCommand extends Command { private var time:Number = 0; private var timerID:Number = null; private var message:String; /** * コンストラクタ * @param time タイマーの時間 * @param message トレースメッセージ */ public function HogeCommand(time:Number, message:String) { super(this); this.time = time; this.message = message; } /** * 実行 */ public function execute():Void { if (this.timerID == null) { this.timerID = setInterval(this, "hoge", this.time); } } /** * タイマー動作 */ private function hoge():Void { trace("hogeCommand complete : " + this.message); clearInterval(this.timerID); this.dispatchEvent({type:"onComplete"}); } }
最後に、CommandQueue クラス。enqueue で リストに Command を追加して、dequeue で Command を順番に実行する。リストが空になると onEmpty イベントを発行する。
import mx.utils.Delegate; import mx.events.EventDispatcher; /** * コマンドキュー */ class CommandQueue { public var addEventListener:Function; private var dispatchEvent:Function; private var commands:Array; /** * コンストラクタ */ public function CommandQueue() { EventDispatcher.initialize(this); this.commands = new Array(); } /** * キューにコマンドを追加 * @param command 追加するコマンド */ public function enqueue(command:Command):Void { this.commands.push(command); } /** * キューからコマンドを取り出して実行 * ※リストが空になるまで再帰的に実行する */ public function dequeue():Void { if (this.commands.length > 0) { var nextCommand:Command = Command(this.commands.shift()); nextCommand.addEventListener("onComplete", Delegate.create(this, dequeue)); nextCommand.execute(); } else { this.dispatchEvent({type:"onEmpty"}); } } }
そして、いよいよ実行!
import CommandQueue; import HogeCommand; //インスタンスを作成 var commandQueue:CommandQueue = new CommandQueue(); var hoge1:HogeCommand = new HogeCommand(2000, "hoge1"); var hoge2:HogeCommand = new HogeCommand(1000, "hoge2"); //キューのリスナを作成 var commandQueueListener:Object = new Object(); commandQueueListener.onEmpty = function():Void { trace("commandQueue is empty !"); }; commandQueue.addEventListener("onEmpty", commandQueueListener); //実行! commandQueue.enqueue(hoge1); commandQueue.enqueue(hoge2); commandQueue.dequeue();
以下の結果を得られる。
hogeCommand complete : hoge1 hogeCommand complete : hoge2 commandQueue is empty !
hoge1 の終了を待ってから hoge2 を実行するところがポイントになる。HogeCommand ではタイマーで擬似的に遅延を発生させたけれど、最初に書いた MovieClip.loadMovie や XML.load を Command パターンで実装するには、LoadMovieCommand 等のラッパークラスを作って、execute から loadMovie を行うようにする。そして onLoad イベントが呼び出されたら onComplete イベントを発行すればよい。