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 イベントを発行すればよい。