gabriel montagné láscaris comneno

Cairngorm Queue Commands for Deferred Command Chains

Update Wednesday June 3, 2008:

The approach that I originally presented in this post was derived from a non-flex, pure ActionScript solution. I still endorse the pure ActionScript command queues for non-flex non-cairngorm applications. (Check them out here http://rojored.com/#pure-as3-commands). But I now realize that the translation to Cairngorm of this concept was not entirely correct (although it did save my ass more than a couple of times).

I realized this after Thomas Burleson presented the Universal Mind Cairngorm Extensions on the flex show . I asked him to check out these classes that I'd presented here and he was cool and commented on them (he suggested that I used lazy instantiation for the commands), and also pointed me to his EventGenerator class. This EventGenerator class correctly queues the events, the signals, that trigger the commands, not the commands themselves (like I'd been doing). This makes much more sense from a Cairngorm point of view.

As an exercise, and to try out these extensions, I rewrote the small app that runs this blog. I used the EventGenerator for the startup sequence. I do want to do a more complete post on this topic, but for now, check the source code out if you want to see how I did it:

Here's my EventGenerator subclass: http://code.google.com/p/rojored/source/browse/trunk/docs/examples/sites/rojored.com/src/com/rojored/control/events/startup/StartupEventGenerator.mxml Here is where it's instantiated and run http://code.google.com/p/rojored/source/browse/trunk/docs/examples/sites/rojored.com/src/Main.mxml#84. From here you can browse the rest of the source code if you need more context: http://code.google.com/p/rojored/source/browse/trunk/docs/examples/sites/rojored.com

Original post (from January, 2008):

Q: Hey, G, where were you loading your data xml?
A: Well, on the onFontsLoaded() handler, where else??!!

Ok, we're writing this Cairngorm application, we need to do several things in order, but we have to wait for each to finish before continuing with the next. And we might even want not to continue if any one of them fails.

The usual workaround is just to start flipping flags on the model, and have views dispatch the appropriate events when the flags they are bound to are raised. If it were just weird... but it's worse: it's sad to go back to when there's an additional step to add or you want to change the order of the commands.

So I wrote a very simple queue package for Cairngorm Commands. It's based on the IResponder interface of the stock flex mx.rpc package, which the sub-commands use to tell their grouping command that they are done, that the next command can now be executed.

We fire the queue command using a regular Cairngorm event. On the execute of that command we run the addCommand method to add each queable subcommand that we want to run in the order in which we want to run them. Finally, we run executeNextCommand which executes the first command.

Once that command is done, it lets the main command know so that it can run the next one automatically.

This is starting to sound complicated... but it's actualy very easy to use. Grab the code here and take a look at this minimalistic example to see how to hook them up:


package com.example.commands.episode {
    
    import com.adobe.cairngorm.control.CairngormEvent;
    import com.rojored.commands.queue.AbstractQueueCommand;

    public class DownloadEpisodeCommand extends AbstractQueueCommand {

        override public function execute(event :CairngormEvent) :void {

            addCommand(new AddNewDownloadCommand(),     event);
            addCommand(new ValidateDownloadCommand(),   event);
            addCommand(new PurchaseProductCommand(),    event);
            addCommand(new BeginDownloadCommand(),      event);

            executeNextCommand();

        }
    }
}

Here's how that queable command could look like:


package com.example.commands.episode {

    import com.adobe.cairngorm.control.CairngormEvent;
    import com.rojored.commands.queue.AbstractQueableCommand;

    public class PurchaseProductCommand extends AbstractQueableCommand {

        override public function execute(event :CairngormEvent) :void {

            // some asynchronous logic
            // once we've done what we needed, waited for whatever
            // we needed to wait to, we call: 

            result(null);

            // or, maybe even 
            // fault(someErrorInformation);
            // if this didn't go well.

        }
    }
}

In this example, we're just passing down the received event as it came to the different commands. Of course, you'd better use a more specific event type with typed properties will make this cleaner. But passing the same instance of the event to all the commands allows it to be modified by one the commands, and because we're passing the same reference to all of them, the other commands down the line will get the modified event with the completed data. And for them, it'd be exactly the same as if you'd dispatched that particular event with the complete data from wherever directly through the FrontController.

01/30/2008 21:51:48