My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
Queue  
processes nested asyncronous operations in succession
Featured
Updated Jun 20, 2010 by maxkugland@gmail.com

The queue package enables to build a tree like structure of asyncronous operations which are processed consecutively. This can simplify your code a lot as dealing with the asyncronous nature of actionscript is one of the more complex parts in developing a flash application.

So first we create a new Q instance with the id "root" and register for all events it distributes.

	var q : IQ = new Q("root");
	q.register(QEvent.START, onStart);
	q.register(QEvent.PROGRESS, onProgress);
	q.register(QEvent.ERROR, onError);
	q.register(QEvent.COMPLETE, onComplete);

next we need something to add to our q. That would be an IQable or an IQ. splinklibrary already contains some classes which implement IQable . For instance QTween and QLoader. So we add these to the q. Eventually we start the q.

	q.add(new QTween(something).add(QTweenConst.ALPHA, 1, 0).duration(200));
	q.add(new QLoader(new URLRequest("some.png"), null, "png1");
	q.add(new QLoader(new URLRequest("another.png"), null, "png2");
	q.add(new QTween(something).add(QTweenConst.ALPHA, 0).duration(200));
	
	q.start();

Now the q processes the added IQable implementations in the order they were added. So firstly something's alpha property is tweened from 0 to 1, then two png images are loaded and finally something's alpha propery is tweened from 1 to 0.

As we registered for serveral events here are the handler functions:

There is the start handler which is invoked when the start method of the q is executed and q starts processing.

private function onStart(e:QEvent):void {
     trace("q " + e.id + " has started");
}

There is the complete handler which retrieves both of the earlier enqueued loaders and adds their content to the display list.

private function onComplete(e : QEvent) : void {
    var q : IQ = IQ(event.source);
	
	var loader1 : QLoader = QLoader(q.getForId("png1"));
	addChild(loader1.getContent());
	
	var loader2 : QLoader = QLoader(q.getForId("png2"));
	addChild(loader2.getContent());
}

There is the error handler which is invoked if an item in the Q distributes an error event. For instance QLoader distributes an QEvent.ERROR event if the loading of a file fails. Then the Q skips the item which distributed the error and continues with the next item.

private function onError(e:QEvent) : void {
	trace(e.errorMessage);
}

And finally there is the progress handler which extracts information on the progress of the Q and the progress of the item which is currently processed by the Q.

private function onProgress(e:QEvent) : void {
	var qp : String = e.queueProgress.current + "/" + e.queueProgress.total + " -> " + e.queueProgress.percent;
	var ip : String = e.progress.current + "/" + e.progress.total + " -> " + e.progress.percent;
	trace(qp);
	trace(ip);
}

Now as an IQ's add method takes IQable implementations, it also takes IQ implementations as the IQ interface extends IQable. This enables to nest queues:

	var anotherQ : IQ = new Q("another-queue");
	anotherQ.add(new QLoader(...));
	anotherQ.add(new QLoader(...));
	
	var andAnotherQ : IQ = new Q("and-another-queue");
	andAnotherQ.add(new QTween(...));
	andAnotherQ.add(new QLoader(..., "some-nested-id"));
	
	anotherQ.add(andAnotherQ);
	q.add(anotherQ);

In order to retrieve an item from a nested IQ, lets say the item with the "some-nested-id" it is not sufficient to call the getForId method on the root q as getForId only looks at the Qable items which have been added directly the the q, and ignores Qable items in nested queues. However there is a simple option to get hold of the item. The Q implementation of the IQ interface contains a static getForId method which recursively searches the item with the provided id in the given IQ and the nested IQ's as well.

private function onComplete(e:QEvent) : void {
	var q : IQ = IQ(e.source);
	q.getForId("some-nested-id"); // dosn't work, because the item is not direct member of the root q
	// but this works:
	Q.getForId(q, "some-nested-id"); 
}

If we want to know when a specific item completes we can register a complete listener on the item like so:

	q.add(new QLoader(...)).register(QEvent.COMPLETE, function(e:QEvent):void {
		trace("I am QLoader: " + e.source is QLoader)
	};

If we need to do something with all enqueued items (yes the nested items, too) we can use the statc visit method of the Q class:

Q.visit(q, function(qable : IQable):void {
	trace("hello " + qable.id);
});

At last here is how to implement your own IQable. To make this a pleasant experience, the queue package contains a base class called Qable which implements the IQable interface and saves you from writing too much boilerplate:

	public class AsyncQable extends Qable {
		private var _timer : Timer;
		private var _progressEvent : QEvent;

		public function AsyncQable() {
			super("AsyncQable");
			_timer = new Timer(10, 8);
			_progressEvent = new QEvent(this, QEvent.PROGRESS, id);
		}

		override protected function doStart() : void {
			_timer.addEventListener(TimerEvent.TIMER, run);
			_timer.addEventListener(TimerEvent.TIMER_COMPLETE, onComplete);
			_timer.start();
		}

		private function run(event : TimerEvent) : void {
			progress();
		}

		private function progress() : void {
			_progressEvent.progress.current = _timer.currentCount;
			_progressEvent.progress.total = _timer.repeatCount;
			distribute(_progressEvent);
		}

		private function onComplete(event : TimerEvent) : void {
			_timer.removeEventListener(TimerEvent.TIMER, run);
			_timer.removeEventListener(TimerEvent.TIMER_COMPLETE, onComplete);
			complete();
		}

		override protected function doStop() : void {
			_timer.stop();
		}

		override protected function doReset() : void {
			_timer.reset();
		}
	}
}

Sign in to add a comment
Powered by Google Project Hosting