OverviewWith this new article we would like to introduce you to one of the most important aspects of the GuggaFF, the concept of tasks and sequences and their applications. We will start with one example and how it is usually handled in Flash.
Let’s examine an abstract GUI application with dynamic content implemented in Flash. One of our first tasks will be to extract the application data from our data source. It doesn't matter what of kind of data source you are using - http service, web service or simple xml file it will take a while to load and process the data. Fortunately data loading in the Flash runs in background thread, so we can implement a loading animation which will capture the user attention during these tasks. After the data is loaded we'll want to populate the controls responsible for its presentation. This example may scale to a situation where depending on the size of the data the process may need to be distributed on more than one frame to achieve the desired performance.
In more complex sites we will need to implement the ability to have more then one view (also known as sections in GuggaFF) or different states of the view. Depending on user interactions we will need to switch the current section or to change its internal state. Often this operations are composition of loading and transition animations, preloading of external assets etc.
Common problemsDuring the implementation of a site in Flash we will need to handle a lot of asynchronous operations. The base approach is to register an event handler for the completion of the operation and use it to continue our work after the asynchronous task is finished. If you are using the event delegation mechanism of mx framework it would be something like this:
private var mImageLoader : ImageLoader;
private function loadContent(aImageUrl : String) : Void
{
mImageLoader.addEventListener("completed", Delegate.create(this, onImageLoaderCompleted));
mImageLoader.load(aImageUrl);
}
private function onImageLoaderCompleted() : Void
{
}
Now imagine that we want to implement a functionality which is a sequence of asynchronous operations. If we scale this approach the resulting code will be:
private var mAnimation1 : IAnimation;
private var mAnimation2 : IAnimation;
private var mAnimation3 : IAnimation;
private function initUI() : Void
{
mAnimation1.addEventListener("completed", Delegate.create(this, onAnimation1Completed));
mAnimation2.addEventListener("completed", Delegate.create(this, onAnimation2Completed));
mAnimation3.addEventListener("completed", Delegate.create(this, onAnimation3Completed));
}
private function startAnimationsSequence() : Void
{
mAnimation1.start();
}
private function onAnimation1Completed() : Void
{
mAnimation2.start();
}
private function onAnimation2Completed() : Void
{
mAnimation3.start();
}
private function onAnimation3Completed() : Void
{
}
Also if we need to wait for more than one event before we can execute some function one of the possible solutions is:
private var mImageLoader1 : ImageLoader;
private var mImageLoader2 : ImageLoader;
private var mIsImageLoader1Completed : Boolean;
private var mIsImageLoader2Completed : Boolean;
private function initUI() : Void
{
mImageLoader1.addEventListener("completed", Delegate.create(this, onImageLoader1Completed));
mImageLoader2.addEventListener("completed", Delegate.create(this, onImageLoader2Completed));
}
private function loadConent(aUrl1 : String, aUrl2 : String) : Void
{
mIsImageLoader1Completed = false;
mIsImageLoader2Completed = false;
mImageLoader1.load(aUrl1);
mImageLoader2.load(aUrl2);
}
private function onImageLoadersCompleted() : Void
{
}
private function onImageLoader1Completed() : Void
{
mIsImageLoader1Completed = true;
if(mIsImageLoader2Completed)
{
onImageLoadersCompleted();
}
}
private function onImageLoader2Completed() : Void
{
mIsImageLoader2Completed = true;
if(mIsImageLoader1Completed)
{
onImageLoadersCompleted();
}
}
If we combine this examples and try to scale them the result will be a big amount of spread and tangled code. So we need to find a way to generalize the management of computations which are combination of asynchronous operations.
Tasks in GuggaFFThe base unit for solving these kind of problems in GuggaFF is the task. The task is abstraction of operation which has determined begin and end. It is represented by the interface gugga.common.ITask.
In GuggaFF we have a lot of implementations of this interface – ImageLoder, XMLLoader, TimelineAnimation, PropertiesTweenAnimation. Also we provide some helper tasks which simplify and reduce the resulting code in this approach – ExecuteMethodTask, ExecuteAsynchMethodTask, SingleExecutionTask.
So once we have tasks we need a way for combining them. The basic composition of tasks is the linear sequence:
This composition can be implemented with the gugga.sequence.TaskSequence class. It uses an single-linked list for managing the task execution. As we have in the Composite design pattern TaskSequence is also task. When we start the sequence, the head of the list is stated too. Every task in the list waits for the end of his predecessor and then it is started. This iteration continues until the last task in the sequence has been completed, then the whole sequence completes.
If we rewrite the previously mentioned example of sequence of animations with the TaskSequence class it will look like:
private var mAnimation1 : IAnimation;
private var mAnimation2 : IAnimation;
private var mAnimation3 : IAnimation;
private function startAnimationsSequence() : Void
{
var animationSequence : TaskSequence = new TaskSequence();
animationSequence.addTask(mAnimation1);
animationSequence.addTask(mAnimation2);
animationSequence.addTask(mAnimation3);
Listener.createSingleTimeListener(
new EventDescriptor(animationSequence, "completed"), Delegate.create(this, onAnimationSequenceCompleted));
animationSequence.start();
}
private function onAnimationSequenceCompleted() : Void
{
}
Before describing the other composite task in GuggaFF – gugga.sequence.TaskManager we will need to introduce the abstraction for precondition.
In GuggaFF it is abstracted by the gugga.common.EventDescriptor class. This class is a pair of event name and an object which raises this event. So the EventDescriptor class exactly describes a moment in the lifetime of concrete object.
GuggaFF provides a mechanism for composing preconditions with the gugga.sequence.PreconditionsManager class. This manager observes a collection of preconditions and raises an event when all of them are met.
If we use PreconditionsManger in the example of waiting for completion of two loaders it will be something like:
private var mImageLoader1 : ImageLoader;
private var mImageLoader2 : ImageLoader;
private function setConent(aUrl1 : String, aUrl2 : String) : Void
{
var preconditionManager : PreconditionsManager = new PreconditionsManager();
preconditionManager.add(new EventDescriptor(mImageLoader1, "completed"));
preconditionManager.add(new EventDescriptor(mImageLoader2, "completed"));
Listener.createSingleTimeListener(
new EventDescriptor(preconditionManager, "preconditionsMet"), Delegate.create(this, onImageLoadersCompleted));
mImageLoader1.load(aUrl1);
mImageLoader2.load(aUrl2);
}
private function onImageLoadersCompleted() : Void
{
}
When we have the idea for precondition, we can easily describe the TaskManager class as a composite task which manages the execution of its children by preconditions. We can think that the TaskManager is a web of tasks connected by preconditions.

According to Composite Pattern, the TaskManager is task itself. When the manager is started, all of its starting tasks are started too. When all the preconditions for certain task in the web are met and all of his predecessors are completed then the task is started. The manager is completed when all final tasks are completed and all final precondition are met.
As you see TaskManager lets you to solve complex situations which are combination of all previously mentioned common problems. A simple example of his usage can be illustrated by extending the example for usage of PreconditionManager by adding an animation which should be started after completion of the loaders. So the resulting code will be:
private var mImageLoader1 : ImageLoader;
private var mImageLoader2 : ImageLoader;
private var mAnimation : IAnimation;
private function setContent(aUrl1 : String, aUrl2 : String) : Void
{
var taskManager : TaskManager = new TaskManager();
taskManager.addStartingTasks([mImageLoader1, mImageLoader2]);
taskManager.addTaskWithPredecessors(mAnimation, [mImageLoader1, mImageLoader2]);
taskManager.setFinalTask(mAnimation);
Listener.createSingleTimeListener(
new EventDescriptor(taskManager, "completed"), Delegate.create(this, onTaskManagerCompleted));
taskManager.start();
}
private function onTaskManagerCompleted() : Void
{
}
Stay tuned for more articles related to interruption of tasks, deeper look into web of tasks (TaskManager) and more advanced techniques related to their usage.