Support Staff3 Posted by Shaun Smith on 21 Dec, 2009 06:23 PM
We've had some chats on the discussion group around Commands -
but mostly around Async Commands. As Joel mentions, an
extension/addon would be the way to go here. I've got a bit of a
case of "analysis paralysis" trying to figure out the best way to
Command History (undo/redo)
Command Chaining / Macro Commands
If you have an immediate need for Macro Commands, this can be
achieved easily by extending Command into MacroCommand and adding a
method like this:
protected function callCommand(commandClass:Class, event:Event = null):void
var eventClass:Class = Object(event).constructor;
command = injector.instantiate(commandClass);
command = injector.instantiate(commandClass);
UPDATE: Robotlegs 1.1.x introduces
ICommandMap.execute() which makes the info above unnecessary -
commands can be directly executed through the Command Map without
being bound to events. ICommandMap.detain() and release() have also
been introduced to easily enable Async Commands.
4 Posted by Rigard Kruger on 16 Mar, 2010 10:37 AM
Well this isn't very elegant, but seems to produce the expected
results (ie., calling them in the right order) if you aren't after
asynchronous command stringing. In your MacroCommand, use the
following (the oneshot parameter will unmap the simple commands so
there should be no conflict with anything else):
Support Staff7 Posted by Shaun Smith on 15 Jul, 2010 01:31 PM
Probably not quite the answer you were looking for, but
Robotlegs 1.1.x introduces ICommandMap.execute() - commands can be
directly executed through the Command Map without being bound to
events. ICommandMap.detain() and release() have also been
introduced to easily enable Async Commands.
Personally I only develop things when I need them, and so far I
haven't had a need for the PureMVC style Async Macro commands.
If anyone does develop a nice utility for this we'd be happy to
make it official by forking it into the Robotlegs GitHub
Would I be correct in assuming that calling execute does not require the executing command to be added to the ICommandMap beforehand? It will simply be instantiated, injected and executed (assuming it extends Command)?
Also, is there any examples of using detain() and release()?
Support Staff9 Posted by Shaun Smith on 15 Jul, 2010 02:07 PM
Yes, that's correct. Except that Robotlegs does
not require you to extend Command - any class that
declares an execute() method will work. Extending Command simply
gives you some commonly needed dependencies; you could easily
inject these yourself by declaring dependencies on the things you
Regarding detain() and release():
// In some command
override public function execute():void
// hold on tight! pls don't GC me, kthnxbye
// kick off some async process
protected function someCallback():void
// it's time to let go, yo
Support Staff15 Posted by Joel Hooks on 11 Aug, 2010 05:00 PM
There is no RL specific implementation, but you could technically use external libraries to achieve this sort of functionality. There is no ned to fork Robotlegs to build this sort of thing. It is appropriate for an external utility, as is the general practice for extending/adding to Robotlegs.
It'd be great if you picked it up. Keep us updated.
Riddle me this. So I'm working on a macro command (both sequence and parallel) where you would register subcommands in a manner somewhat like so:
If it's asynchronous:
addCommand(new MyGoEvent(MyEvent.GO), MyCompleteEvent.COMPLETE, MyCompleteEvent);
If it's synchronous:
When it's the command's turn to execute, the macro simply dispatches the event (it assumes you've wired up the command in the context). If it's asynchronous, the macro command watches for the specified completion event to determine that the command has completed execution.
That's all good. Where is gets a little tricky though is when you have a nested command setup like this:
The sequence command is nested within the parallel command. If both instances of CommandB are executing at the same time and one completes, the parallel and sequence commands don't know if the completion event corresponds to the instance of CommandB that that macro "encapsulates". In other words, maybe the CommandB that the sequence command initiated completed but the parallel command thinks the completion event pertains to the CommandB that the parallel command initiated.
I realize this issue is presented in RobotLegs in general and isn't specific to macro commands, but I'd optimally like to handle it without forcing CommandB to extend any specific class or dispatch a specific event that supports a token.
Support Staff20 Posted by Joel Hooks on 14 Aug, 2010 04:47 AM
Awesome, thanks :>
I haven't had a chance to review the code yet. I am on a roadtrip with my wife and kids, but just from a glance it looks very clean and simple. I'd like to see it unit tested if you have the stomach for that sort of thing. Keeps in the spirit of the framework that way!
@Ondina Yeah it's been crunch time on a project I'm on but when I get a break I'll dig into the issue a bit more.
@Chase I don't think you need those properties that Ondina mentioned. What's the purpose of them? If it's just for looping back over each of them in ParallelCommand, just handle it differently. You don't have to loop back through all the commands and see if they failed each time a command executes. When a subcommand calls your ParallelCommand's subcommandIncomplete() function, just mark your ParallelCommand as "having a least one failure" and be done with it.
@Joel Thanks! I will add in some unit testing soon (hopefully in the next few days)
@Ondina So, thanks to your comment I think I come up with some changes that are better than they where before. Now there is an "executionStatus" var that has: waiting, executing, completed, failed.
In addition to that, I made it so that each command can watched for each of these execution status changes. I didn't want anything that extends command (Async, Macro, Parallel, Sequence) to dispatch events themselves, because Command itself doesn't do that and I don't think it is intended too. So, when each command is added, it returns the subcommandDescriptor class that contains all of the data about the command to be executed (Cmd Class, Payload, etc..) as well as the status of that commands execution. That subcommandDescriptor now dispatches events on any status changes. There are updated examples of this in the example project.
@Aaron I actually definitely need some type of indicator telling me what the status of each command. For example, that way I can wait for all parallel subcommands to complete their execution before the parallel command can recognize it is done. Hopefully with the change to using "executionStatus" will resolve any confusion though.
I also want to keep that status of each command on its subcommandDescriptor. That way when the subcommandDescriptor dispatches events about the status of its command, it is the only source of authority for the actual status.
Thanks for the help on marking a flag for having at least one failure, I just pushed a commit that incorporates that.
@Ondina Thanks, I think it is a good way to handle individual command instance triggers
@Aaron, I don't think that there really needs to be two different libraries, I would like to merge our two and collaborate on any differences. Eh? Feel free to fork mine and I think that it could result it a better finished product.
Chase, I'd love to have a single library but I'm having difficulty finding something to merge from CommandLib that would improve the codebase of Macrobot. There are some fundamental elements of CommandLib that are impairing:
The developer has to extend AsyncCommand if they're going to do anything asynchronously (rather than implementing an interface).
Stores unnecessary information regarding command status (I mentioned this in my previous comment).
Manipulates arrays unnecessarily.
Uses events unnecessarily.
Loops through arrays unnecessarily.
Exposes and touches command descriptors in more classes than necessary.
I did some quick benchmarking in an AS project by executing 1000 commands (that extend AsyncCommand but are not truly asynchronous--to keep timer overhead out of the equation). I found the following on average:
I also attempted to run a nested command benchmark by running a sequence command with 100 child parallel commands with each parallel command having 10 async commands like this:
for (var i:uint = 0; i < 100; i++)
parallel = new ParallelCommand();
for (var j:uint = 0; j < 10; j++)
It broke on CommandLib though because the library only injects into non-"programmatic" commands and since ParallelCommand (programmatic in this case) is dependent upon commandMap being injected, it throws a null pointer.
Macrobot runs the procedure in 27ms.
There's also twice as much code in CommandLib with no additional functionality in my understanding. I want to be sensitive but I don't want to beat around the bush either. I would literally be copying all my code into your repo. If you see anything that would improve Macrobot though I'm all ears.
Hey guys, thank you both for tackling this - it's always great to see people put effort into open source endeavors. I'm a little saddened by the direction the conversation has taken however.
I haven't had a chance to review either codebase, and even if I had, I'm not the best person to do so because I haven't come across a direct need for this kind of utility in any of my projects (so far). With that in mind:
@Chase: I wouldn't be too offended by Aaron's review. In fact, getting a code review, even if all the points are negative, is almost always a good thing. It takes guts to put code online for criticism, I understand that. But you have to follow that up with the ability to accept criticism gracefully. He could have been more sensitive, yes, but ultimately it's up to you to decide how you accept criticism: either you take it personally, or you use it to improve your code.
@Aaron: As I said, I haven't actually reviewed either codebase, but for any RL utility to be taken seriously it must have a set of unit tests to prove that it works.
Personally, I hope that you'll both reconcile your differences and decide to collaborate. But, even if you don't, I still think this is a good opportunity to sharpen your solutions by competing (in a healthy manner of course!).