Lifecycle of a Command.

tzamora's Avatar

tzamora

20 Aug, 2010 03:04 AM

Hi.

I believe my question is simple. In the Best Practices docs it says:

Commands are short-lived stateless objects. They are instantiated, executed and then immediately disposed of. Commands are only executed in response to framework events and should never be instantiated or executed by other framework actors.

So exactly when the Command is disposed of ? I want to create a command called AssetsLoaderCommand that will have a lot of loaders, so this command will call in the execute method a lot of .load .... Can I be sure that all my loaders will finish their work before the Command is killed?

Thanks in advance.

  1. 1 Posted by Abel de Beer on 20 Aug, 2010 10:33 AM

    Abel de Beer's Avatar

    Hi tzamora,

    The idea behind the Command pattern is that it contains one uniform method, execute. This method is no different than any other method of any class, which means that once its operations are finished, the method is finished.

    There are a few things here to keep in mind:

    1. If you're talking about garbage collection, it is the same as with other objects: if the Command instance still holds references to other objects (for example through addEventListener), it will not be garbage collected until the operations are complete.

    2. Robotlegs has no default implementation of the Command's execute method (L45). The CommandMap just checks whether the class to be mapped contains an execute method (L202), and when the event it's mapped to is dispatched, a new instance of the class is created and the execute function is called.

    3. This is slightly off-topic, but important to realize: when mapping Command classes to events (for example in the RL Context), it is possible to set the oneShot:Boolean property. When set to true, the mapping is removed after the Command has been executed (L227). When set to false (default), the Command will be executed every time the mapped event is dispatched.

    So, to answer your question more concretely: if you configure you event listeners correctly, the loading of assets will happen the same way you'd expect when called from other places.

    (I'm learning Robotlegs myself so I hope I've said everything correctly. If not, please correct me!)

    I hope this clears it up for you.

    Abel.

  2. 2 Posted by Nikos on 20 Aug, 2010 11:04 AM

    Nikos 's Avatar

    I don't think this stops GC

    if the Command instance still holds references to other objects it may still be GCed.

    I think your application needs reference to the command, which you can do by using commandMap.detain(command)

  3. 3 Posted by tacklemcclean on 20 Aug, 2010 11:24 AM

    tacklemcclean's Avatar

    What about Injected variables?

    If a Command has a service and a model injected via the [Inject] metatag, is the Command ever GC'd?

  4. 4 Posted by Nikos on 20 Aug, 2010 11:25 AM

    Nikos 's Avatar

    I think you still need to use commandMap.detain

    FYI

    Command.as [Inject] public var commandMap:ICommandMap;

    Context.as
    protected function mapInjections():void

        {
            injector.mapValue(IReflector, reflector);
            injector.mapValue(IInjector, injector);
            injector.mapValue(IEventDispatcher, eventDispatcher);
            injector.mapValue(DisplayObjectContainer, contextView);
            injector.mapValue(ICommandMap, commandMap);
            injector.mapValue(IMediatorMap, mediatorMap);
            injector.mapValue(IViewMap, viewMap);
            injector.mapClass(IEventMap, EventMap);
        }
  5. 5 Posted by tzamora on 20 Aug, 2010 02:05 PM

    tzamora's Avatar

    Hi

    Thanks for everyone.

    I created the command and I had this issue (none of my event listener where working) but I have already solved, my mistake were that I created each loader event with useWeakReferences = true:

    _animalsLoader.contentLoaderInfo.addEventListener(Event.INIT, animalsLoaderInitHandler, false, 0, true);
    _animalsLoader.load(new URLRequest("assets/swf/animals.swf"));

    instead the event must be created without useWeakReferences = true ....

    _animalsLoader.contentLoaderInfo.addEventListener(Event.INIT, animalsLoaderInitHandler);
    _animalsLoader.load(new URLRequest("assets/swf/animals.swf"));

    Niko BTW can you explain the commandMap.detain(command) when and how I use it. Is in my context? Maybe is something usefull for me to know later.

    (by the way I'm a non english speaker so sorry for any mistake :P)

  6. 6 Posted by Nikos on 20 Aug, 2010 02:13 PM

    Nikos 's Avatar

    use

    commandMap.detain(this)

    inside your command before you exit your exceute method if your waiting for some async proc to finish

    fyi see Command.as

     */
    public class Command
    {
        [Inject]
        public var contextView:DisplayObjectContainer;
    
        [Inject]
        public var commandMap:ICommandMap;
  7. 7 Posted by Paul Robertson on 20 Aug, 2010 05:10 PM

    Paul Robertson's Avatar

    The others have given some valuable responses, but I wanted to add in a
    couple of points that I think are important.

    (Disclaimer: this is all my opinion based on what I've observed, read,
    and done, and Shaun, Joel, Stray, or any of the others who frequent
    these forums may disagree. Which I'd love to hear, so I can revise my
    opinion =)

    I. Robotlegs is intentionally designed to be flexible. This is really
    cool and powerful, but it also means that different examples, blog
    posts, etc. demonstrate different approaches to accomplishing the same
    task. And that your way of working will invariably be different from
    everyone else's in some subtle or big ways.

    II. The "Best Practices" doc is primarily focused on the default MVC+S
    implementation that is built into Robotlegs. In the MVC+S approach as
    designed and described in the Best Practices, commands don't perform
    asynchronous actions. (More specifically, they may trigger an
    asynchronous action, but they don't handle the response from the
    action.) That's why it says that they're short-lived and designed to get
    garbage collected quickly. Commands basically have three possible things
    that they can do:
         - Perform an operation on a model instance such as setting a
    property or calling a method
         - Triggering a service call by calling a method on the service instance
         - Trigger other commands (or other changes in the app) by
    dispatching an event

    So far I've stuck to this, so my commands often do only one of these,
    and sometimes they do several things that are combinations of those
    three types of actions. But they don't do anything else (e.g. no event
    handling).

    The first of those, operating on a model, often has some side effect
    (e.g. you want to update the view) so the model will dispatch an event.
    That event is then picked up by mediators to update the view, or it
    might trigger an additional command.

    The second of those, triggering a service, often has some result. For
    example, my services usually retrieve some external data, often
    asynchronously. Internal to the service I have event handlers that get
    the results back. The service class then does one of a couple of things
    (again, depending on how you prefer to structure things):
    - The service has a reference to a model object (via injection), so it
    sets the result data on the model directly.
    - The service class dispatches an event (usually containing the result
    data in the event object). That event might trigger an additional
    command, and/or mediators might be listening for the event and use the
    result data to update the view.

    III. Having said all that, many people have wanted to have commands
    perform asynchronous operations directly rather than delegating to a
    service. For example, in your app you might have the possibility of
    making multiple calls to the same service, where you want to channel the
    results of each call back to different parts of your app rather than all
    results back to all parts of the app. One general approach to solving
    that need is to make an asynchrous command where the command actually
    triggers the action and registers a listener to get the result. Since
    Robotlegs is designed to be flexible, you can (and people have) come up
    with variations on the MVC+S structure to support that need. If you
    prefer to work that way, awesome! I definitely want to look into these
    solutions and try them out, though I haven't had time to do so yet. But
    here's my attempt at a list:

    - One approach is to use some sort of "promise" (responder or async
    token) technique to tie a particular call to the service back to the
    source of the call. Shaun has written about that here:

    http://knowledge.robotlegs.org/discussions/questions/215-parallel-services-async-commands-etc#comment_2221769

    - Another approach to keeping a command around for some reason is to use
    detain(), as Nikos mentioned.

    - Chase Brammer and Aaron Hardy both came up with add-on utilities that
    (in my limited understanding) allow you to do asynchronous commands
    among other things. I believe they are looking into combining their
    efforts in some way so I'm not sure where things will ultimately live,
    but for now here are some links to their projects:

    http://github.com/Aaronius/robotlegs-utilities-Macrobot
    http://github.com/Aaronius/robotlegs-utilities-MacrobotDemo

    http://github.com/cbrammer/robotlegs-utilities-CommandLib
    http://github.com/cbrammer/robotlegs-tests-CommandLib
    http://github.com/cbrammer/robotlegs-demos-CommandLib

    Hopefully that helps clear up the "default" roles that each actor plays,
    which is what the Best Practices attempts to communicate, as well as
    some of the variations that people have developed.

    Paul

  8. 8 Posted by tzamora on 20 Aug, 2010 08:24 PM

    tzamora's Avatar

    Thanks Paul.

    Indeed, when I created this command and began to review the docs and re checked the command responsabilites, I began to doubt my implementation of it.

    I'm right know re thinking my approach, and thanks for all the info, it is truly a very valuable piece of information, I'm still learning but the help is great

    :)

  9. 9 Posted by Nikos on 20 Aug, 2010 09:03 PM

    Nikos 's Avatar

    nice one Paul :)

  10. 10 Posted by Abel de Beer on 20 Aug, 2010 10:52 PM

    Abel de Beer's Avatar

    @Paul: You're totally right, Command objects shouldn't do loading operations directly, but call the assigned methods on Services / Models. Thanks for your contribution!

  11. Support Staff 11 Posted by Shaun Smith on 21 Aug, 2010 03:57 PM

    Shaun Smith's Avatar

    Hi everyone, some great points all round. Just to get back to the original question:

    "So when is the Command disposed of?"

    Firstly, you need to have a good understanding of how Garbage Collection works in the Flash Player: http://www.adobe.com/devnet/flashplayer/articles/garbage_collection...

    If you have a look at the part of the Robotlegs Command Map that creates and executes commands (L170), you will see that once that method has finished executing nobody has a reference to the command instance and it will be free for Garbage Collection.

    So, by default, as soon as the execute() method of a command has executed, that command will be free for GC. But, this is only true if there are no other references to that command instance.

    RL v1.1.2 added detain() and release() to the CommandMap to make it a little easier to work with async processes. You could use them like this:

    override public function execute():void
    {
        commandMap.detain(this);
        // and then kick off some async process...
    }
    
    private function onSomethingHappened():void
    {
        commandMap.release(this);
    }
    

    The implementation is crazy simple: CommandMap#L181

    @tacklemcclean: "What about Injected variables? If a Command has a service and a model injected via the [Inject] metatag, is the Command ever GC'd?"

    Yes. It doesn't matter how many things you inject into your command instance. So long as nobody is holding onto the command instance it will be free for GC.

    However, if you inject a singleton into a command, and then hand that singleton a reference to the command which it stores, then.. well, something is holding on to the command instance and it won't be free for GC. For example:

    [Inject]
    public var service:MyService;
    
    override public function execute():void
    {
        service.load(callback);
    }
    
    private function callback():void
    {
    }
    

    By passing the callback method through to the service, and assuming that the service holds on to it, we have increased the reference count of the command and it won't be free for GC until the service releases it.

    HOWEVER, I am making the assumption that the service was mapped as a singleton (or is otherwise being held onto by some other part of the system). If not (perhaps it was mapped with mapClass) then the only thing holding on to the service would be the command, and the only thing holding on to the command would be the service, and Flash Player's Garbage Collector is clever enough to spot this and GC them both.

  12. 12 Posted by Abel de Beer on 21 Aug, 2010 05:22 PM

    Abel de Beer's Avatar

    Thanks for clearing that up Shaun.
    Time for me to read up on the Garbage Collection process.

  13. 13 Posted by tzamora on 22 Aug, 2010 12:31 AM

    tzamora's Avatar

    Thanks Shaun.

    Well my assumption was bad .... this was not a simple question :P.

    I'm still asking myself what's best, keep the command clean from callbacks (listeners) or create a command with service callbacks ..... It is a matter of likes and dislikes? it is a matter of performance? it is a matter of design .... ?

    Maybe if I keep the commands more simple, the Garbage Collection will happen more smothly .... Oh well ... time to read another post recomended by shaun :)

  14. 14 Posted by Nikos on 23 Aug, 2010 09:38 AM

    Nikos 's Avatar

    dont use callbacks :) cause you'll have to manually remove them in your service singleton on or command and may confuse your team, use detain :)

  15. Support Staff 15 Posted by Shaun Smith on 23 Aug, 2010 04:22 PM

    Shaun Smith's Avatar

    Callbacks are released after they have been called, so it's quite unlikely that you'll have to remove them manually. I use callbacks when it fits the intent of the command.

  16. 16 Posted by Nikos on 23 Aug, 2010 04:27 PM

    Nikos 's Avatar

    oohh thanks for clearing that up

  17. 17 Posted by tzamora on 23 Aug, 2010 04:37 PM

    tzamora's Avatar

    Thanks for everyone ... Its been great to learn all this from all of you.

  18. tzamora closed this discussion on 23 Aug, 2010 04:37 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac