Chaining Commands in Flex/ActionScript 3.0
Jan 4th, 2008 by Mahmoud Arram
In almost all the Cairngorm based applications I’ve worked on, I’ve found it necessary to be able to chain the execution of a set of commands. This is usually necessary in the application’s initialization logic, where one needs to make sure that certain steps happen sequentially.
I’ve seen some chaining implementations that were based on Paul William’s Observe Tag:
<ac:Observe source=”{ model.myProperty }” handler=”{ this.myFunction }”/>
Commands would set certain properties on the model, which would cause the appropriate handlers in the initializer code to be called. However, I’ve always found such logic to be too hard to read and debug.
Other implementations are based on Cairngorm’s SequenceCommand. However, I never liked the SequenceCommand because:
-
1. Each instance of SequenceCommand requires explicit knowledge of the next event/command it needs to fire/execute. This is problematic for two reasons:
a) It violates the Principle of Least Knowledge
b) Sometimes you need to link or unlink new commands in the sequence chain. On other occasions, you might need to reorder the chain. Having to change the nextEvent property on every SequenceCommand is tedious and error prone.
2. You need to explicitly call executeNextCommand() on each SequenceCommand instance. Determining when to exactly do that is not straightforward.
After writing a few initialization chains, we’ve come up with a pretty reasonable appraoch.
Chaining Using Callbacks (Function Pointers)
We provide each command (in the base class AbstractCommand) with callbacks for success and failure. The initializer instantiates each command and assigns it appropriate callbacks.
/**
* Initializes the commands stack
*/
public function init() : void {
var command0:Command0 = new Command0();
command0.resultCallback = invokeNextCommand;
command0.faultCallback = handleFault;
var command1:Command1 = new Command1();
command1.resultCallback = invokeNextCommand;
command1.faultCallback = handleFault;
commands.push(command0);
commands.push(command1);
//start the domino effect
invokeNextCommand(null);
}
In our implementations, the result (success) callback just invokes the next command:
/**
* invokes the next sequential command in the commands array
*
* @param event
*
*/
private function invokeNextCommand(event:*):void {
commandCounter++;
if (commandCounter >= commands.length) {
finalize();
}
else {
(commands[commandCounter] as AbstractCommand).execute(null);
}
}
Some commands require special result handlers. For example, if the success of said command eliminates the need to execute another command in the chain. We’d do such processing in a specialized result callback that would finally call invokeNextCommand().



