Work at SourceForge, help us to make it a better place! We have an immediate need for a Support Technician in our San Francisco or Denver office.

Close

PerformingTasksinSequenceandParallel

### Cairngorm 3 - [ Home ][1] - [ Guidelines ][3] - [ Tools ][4] - [ Libraries Downloads ][2]
### Cairngorm 2 - [ Home ][5] - [ Framework Downloads ][6] - [ Eclipse Plugin ][7]
### Project - [ Source ][8] - [ Bug Database ][9] - [ Submitting a Patch ][10] - [ Developer Documentation ][11] - [ Forums ][12] - [ License ][13]

Performing Tasks in Sequence and Parallel

Introduction

Events are fundamental to Flex. Every component in the SDK dispatches an assortment of them, while application developers regularly create their own to represent user gestures and other significant occurrences. With so many events being dispatched and handled, coordination can become challenging. A class may assume too many responsibilities, such as initiating an asynchronous service call, handling the result, then triggering a subsequent service call and handling that result also.

It is for these reasons that a general task processing library is helpful. Such a library can take care of starting a sequence of asynchronous tasks in the correct order, or setting a number of tasks to execute in parallel (concurrently to one another). A task library can nest tasks beneath one another, so the parent task completes only when all of the children have finished processing. Furthermore, a task library dispatches a common set of events to indicate progress, so higher-level components can operate independently of the specifics of the task being enacted.

Task Libraries

There are several task libraries available for Flex development and some alternative approaches that solve some of the same problems. Three of these are introduced here, but please contact us if there are other alternatives that could be mentioned. The remainder of the article provides some examples of the Cairngorm Task library.

Cairngorm Task

Cairngorm Task is one such library for performing tasks in sequence and parallel. Its main features are described below:

  • Processes tasks in sequence or parallel.
  • Supports nesting of tasks beneath one another.
  • Can be declared in MXML or assembled in ActionScript.
  • Can be bound to a view components using bindings.
  • Dispatches events to indicate progress.
  • Supports conditional tasks that can be skipped.
Spicelib Task Framework

The Spicelib Task Framework is a part of the Spicelib library upon which the Parsley Application Framework is built. This task framework is actually more powerful than Cairngorm Task in several ways, supporting task suspension, resumption, cancellation and more. However, it is not designed for declaration in MXML and does not currently provide bindable properties for tracking the progress of a task in execution. If these features are incorporated into a later version, then the Cairngorm Task library may become redundant.

The Parsley & Spicelib Developer Manual includes a chapter on The Task Framework. The conclusions reached in this article apply also to both Cairngorm Task and the Spicelib Task Framework.

SequenceCommand

The SequenceCommand is a class that is sometimes applied on traditional Cairngorm 1 and 2 projects. Its purpose is to initiate another Cairngorm command when one command finished processing, allowing a sequence of commands to be processed. This approach is considered a bad practice since it gives one command knowledge of the next command, which limits reusability and increases the complexity of commands.

An Example Cairngorm Task

A task may be used wherever there is a need to coordinate multiple asynchronous tasks, processing them in sequence or parallel. It may be used wherever there is the need to start a process that will later complete or generate a fault. A common example is an application start-up sequence. Perhaps an initial service call takes place to fetch the user profile, followed by a number of subsequent requests to fetch data specific to the user's needs. A number of modules and compiled style-sheets may also be loaded during this start-up sequence.

Declaring a Task in MXML

Here is this example task declared in MXML with the Cairngorm Task library:

<task:SequenceTask xmlns:mx="http://www.adobe.com/2006/mxml"
                   xmlns:task="com.adobe.cairngorm.task "
                   xmlns:example="com.adobe.cairngorm.task.example">

    <example:LoadUserProfile/>

    <task:ParallelTask>
        <example:LoadNews/>
        <example:LoadContacts/>
    </task:ParallelTask>

    <task:ParallelTask>
        <LoadModule url="http://domaina/ModuleA.swf"/>
        <LoadModule url="http://domaina/ModuleB.swf"/>
        <LoadStylesheet url="http://domaina/ModuleAStyles.swf"/>
        <LoadStylesheet url="http://domainb/ModuleBStyles.swf"/>
    </task:ParallelTask>
</task:SequenceTask>

When expressed in MXML, a task can be self-documenting. The above example declares a sequence that begins with the LoadUserProfile work-item. When the user profile has been fetched, the LoadNews and LoadContacts task-items will begin in parallel. Then when they have both completed, the loading of a number of modules and style-sheets commences. When those have completed the entire task is complete.

A task composition can also be assembled in ActionScript, as shown in the code excerpt below:

var parallel1:ParallelTask=new ParallelTask();
parallel1.addChild(new LoadNews());
parallel1.addChild(new LoadContacts());

var parallel2:ParallelTask=new ParallelTask();
parallel2.addChild(new LoadModule("http://domaina/ModuleA.swf"));
parallel2.addChild(new LoadModule("http://domaina/ModuleB.swf"));
parallel2.addChild(new LoadStylesheet("http://domaina/ModuleAStyles.swf"));
parallel2.addChild(new LoadStylesheet("http://domaina/ModuleBStyles.swf"));

var task:SequenceTask=new SequenceTask();
task.addChild(new LoadUserProfile());
task.addChild(parallel1);
task.addChild(parallel2);

The above code assembles the same task as the first MXML example. It is a little harder to read because the children have to be constructed and added manually in the correct order. MXML is generally preferred for complex task compositions, although some users may consider this to be "programming in XML" and prefer ActionScript.

Creating and Starting a Task

Once a task has been instantiated, it can be started by calling the start() method:

task.start();

The task will begin processing and continues until it completes successfully or one of the children generates a fault. Both of these outcomes are indicated by events:

  • TaskEvent.TASK_COMPLETE
  • TaskEvent.TASK_ERROR

These events can be handled in MXML using in-line event handlers:

<example:StartupTask id="task"
                     taskComplete="doSomething()"
                     taskError="doSomethingElse()"/>

Or they can be handled in ActionScript:

task.addEventListener(TaskEvent.TASK_COMPLETE, taskCompleteHandler);
task.addEventListener(TaskEvent.TASK_FAULT, taskFaultHandler);
Creating a New Task

The Cairngorm Task library includes a few standard task-items – ParallelTask and SequenceTask – but in most cases developers need to write their own tasks to perform operations specific to their application. For example, the application start-up task composition above included the LoadUserProfile task.

A new task can be written easily by either implementing the ITask interface or extending the Task base class. The latter is the simplest approach, since the base class contains some logic general to all tasks. A concrete class only needs to override the performTask() method then later invoke complete() or fault() when the task has completed or a fault has occurred.
Here's the simples task imaginable:

public class HelloWorld extends Task
{
    override protected function performTask():void
    {
        trace("Hello World!");
        complete();
    }
}

In reality, tasks are likely to invoke asynchronous service calls or integrate with other systems, like a local SQLite database. Here is a more realistic example:

public class LoadUserProfile extends Task
{
    override protected function performTask():void
    {
        var service:HttpService=new HttpService();
        service.url="http://my.domain/userprofile";
        service.addEventListener(ResultEvent.RESULT, onResult);
        service.addEventListener(FaultEvent.FAULT, onFault);
        service.send();
    }

    private function onResult(event:ResultEvent):void
    {
        service.removeEventListener(ResultEvent.RESULT, onResult);
        // do something with result
        complete();
    }

    private function onFault(event:FaultEvent):void
    {
        service.removeEventListener(FaultEvent.FAULT, onFault);
        error(event.fault.message);
    }
}

In this case, the performTask() method creates a new service instance, attaches a couple of event listeners, then invokes the service. When the result event handler is called, the task completes by calling the complete() method; or if a fault occurs instead the error() method is called.

See the example project for more task examples, including use of other events and binding to view components.

Conclusion

Flex applications that need to coordinate multiple asynchronous processes, such as module and stylesheet loading, or sequences of service calls, can benefit from a task library such as Cairngorm Task. A task library can take care of coordination and progress tracking, leaving developers to focus on the specifics of the individual tasks. A general abstraction for task processing also encourages a loosely-coupled design, where one task composition can easily be substituted for another. A collection of general purpose tasks can also be developed and used as building blocks for multiple purposes across different applications.