Futurescale, Inc. PureMVC Home

The PureMVC Framework Code at the Speed of Thought


Over 10 years of community discussion and knowledge are maintained here as a read-only archive.

New discussions should be taken up in issues on the appropriate projects at https://github.com/PureMVC

Pages: 1 [2]
Print
Author Topic: Asynchronous command chaining  (Read 40838 times)
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #15 on: January 04, 2008, 06:08:15 »

Here are those steps rewritten, not losing the flag about whether to create the mailbox:

1: Invoke CreateAccountCommand, with a notification whose type property is equal to either ApplicationFacade. MAILBOX_CREATE or MAILBOX_SKIP.

2: When executed, the CreateAccountCommand calls the UserAccountProxy addUser method, passing the mailbox creation flag to the UserAccountProxy as a parameter

3. UserAccountProxy sets the mailbox creation flag as a proprty of the AsyncToken for the service call in flex, or just holds it as a private var otherwise.

4. UserAccountProxy recieves a call on its result method indicating success.Possibly the fully formed user object was returned.
5: UserAccountProxy sends a user-account-creation-successful notification. It sets the body to a reference to the user object.It also sets the type property of the notification equal to the mailbox flag.
6: The user-account-creation-successfull notification is bound to the CreateMailboxCommand.

5. The CreateMailbox Command inspects the type property and if equal to the constant MAILBOX_CREATE, it invokes the mailbox proxy to create a new mailbox

I've edited this in an absurdly small text box in my WAP browser and will correct later if ths doesn't make sense.

-=Cliff>
Logged
Henk
Newbie
*
Posts: 8


View Profile Email
« Reply #16 on: January 08, 2008, 04:01:06 »

Thanks for the quick reply and sorry for the slow follow up. I was experimenting with your suggestions and some other ideas.

In the previous example, the user account creation complete notification is wired up to the create mailbox command. The solution does work well. However, I do see some disadvantages with this approach:
1. The user account proxy didn't used to have knowledge of mailboxes. Now it needs to add a parameter to pass data around with which it has nothing to do. The more of these workflow-ish commands are added, the more unrelated data has to be passed around.
2. It puts business logic in the notification wiring, where it doesn't belong. The ability to create a mailbox after a user account now also needs to be coded into the ApplicationFacade notification wiring, and in the user account proxy (which passes the data around). For this simple example this is no problem, but for a large application the number these dependencies becomes ugly.
3. I found myself adding notifications for the sake of wiring commands together. Maybe I'm being picky but it feels like the notifications are used to fill in for a missing part.

So, how to pass data around in a manner that scales, while still honoring the stateless command principle?
I'll try by generalized your suggestion a little as follows:
Instead of passing the mailbox info to the create user account proxy, an array is passed with a set of proxy functions and parameters. The result handler of the proxy takes the next proxy function from the array and calls it, passing in the parameters and the remainder of the array. This repeats itself until all proxy functions are called.

With this approach each Command can build a set set of proxy functions that need to run sequentially. The model proxy's don't need to know any details of follow-up action, just support the ability to pass such actions through.

I'm still not completely happy with this solution because passing in the proxy function array adds an artifact to proxy functions that doesn't belong there because it isn't needed to perform its function.  The burden of serializing asynchronous commands doesn't belong in model proxy's nor does it belong in the facade wiring. Still, I take this last approach as the lesser of the two evils.  ;)


Alternatively, a proxy aggregate class can do it all. It can be used to build up multiple proxy commands, can be a responder for each proxy function, and sequentially call the proxy functions.

Thanks for the discussion Cliff and Rhysyngsun. I really appreciate your feedback and hope you can keep sharing your thoughts.
Logged
weblogic
Courseware Beta
Newbie
***
Posts: 1


View Profile Email
« Reply #17 on: January 18, 2008, 01:51:13 »

Maybe this will be helpful(on proxy layer):
http://spicefactory.org/spicelib/docs/as3/1.0/manual/task.php#intro
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #18 on: January 18, 2008, 02:35:10 »

This looks nice. I see potential synergy here. Can someone take a whack at using PureMVC and Spicelib together?

To sum up why I think you'd use Spicelib with PureMVC:

Because the PureMVC framework is not suited for chaining of asynchronous operations, and Spicelib appears to provide a very clean and lightweight solution to the problem.

-=Cliff>
Logged
Joel Hooks
Courseware Beta
Sr. Member
***
Posts: 146


baby steps

 - 46288188  - passport@provinsal.com  - joeltuff
View Profile WWW Email
« Reply #19 on: January 18, 2008, 04:06:44 »

I think I will give it a try this weekend Cliff. It fits nicely into my current project(s). In theory ;)
Logged

http://joelhooks.com - my ramblings about developing with actionscript and python using pureMVC and django respectively.
Henk
Newbie
*
Posts: 8


View Profile Email
« Reply #20 on: January 28, 2008, 10:33:13 »

Thanks for the link Joel,

This would indeed be a solution for the problem I raised.  Spicelib looks nicely done.

In the meantime I've come up with a similar more lightweight approach, based on the AsyncToken pattern.
A class 'SequentialCommandQueue' provides a method to add commands for execution. When a method returns an AsyncToken it is considered a asynchronous command, otherwise a synchronous command. On execute the list of commands is traversed until completion. When a command returns a AsyncToken the queue adds itself as the responder and waits until a response has been received. On success it continues with the next command. On fault it invokes the fault handler.

I'm using this in commands to update models, and in models to call services.

Prototype:
:
interface ISequentialCommandQueue
 {
    public function addCall( func:Function, resultHandler:Function=null, faultHandler:Function=null, ... args ):void;
    public function execute():AsyncToken;
 }
Simple example:
:
public class LogonCommand extends SimpleCommand
{
   override public function execute( note:INotification ):void
   {
      var event:LogonEvent = note.getBody() as LogonEvent;
      var q:SequentialCommandQueue = new SequentialCommandQueue();
      var configProxy:ConfigurationProxy = facade.retrieveProxy( ConfigProxy.NAME ) as ConfigProxy;
      var userProxy:UserProxy = facade.retrieveProxy( UserProxy.NAME ) as UserProxy;
      q.addCall( configProxy.loadConfiguration );
      q.addCall( userProxy.logon, null, onLogonFailure, accountName, password );
      q.addCall( userProxy.retrieveUserData );
      q.addCall( sendNotification, null, null, ApplicationFacade.NTY_LOGON_COMPLETE );
      q.execute();
   }

   public function onLogonFailure( event:Event )
   {
   }
}
Sometimes it is necessary to share state between multiple methods This can be done by create a 'task' class with the methods that share state and add these methods to the queue. Each method can store the state in the task class instance.

My objective was to have something lightweight with a minimal memory footprint and the least amount of overhead. I found this to work really well.

Logged
PhoneTech
Newbie
*
Posts: 8


View Profile Email
« Reply #21 on: April 13, 2008, 04:10:10 »

I implemented the spicelib into my PureMVC project.

I have a command which needs to send and process about 100 request to a httpremoteobject. If I execute all these at the same time, te buffers in apache will run dry and array index out of bounds exceptions are thrown. So to make these request sequential, I use the SequentialTaskGroup from spicelib in my command:

Command excerpt
:
var taskSequence:SequentialTaskGroup = new SequentialTaskGroup();
for(i = 0; i < numOfStatuses;i++)
{
  for(j = 0; j < numOfProjects; j++)
  {
  var status:RemoteStatus = statuses.getItemAt(i) as RemoteStatus;
  var project:RemoteProject = projects.getItemAt(j) as RemoteProject;
  var projectStatus:ProjectStatus = new ProjectStatus(project,status);
  var issueProxy:IssueProxy = new IssueProxy(projectStatus);
  facade.registerProxy(issueProxy);
  taskSequence.addTask(new LoadIssuesTask(issueProxy));
 
  }
}
taskSequence.addEventListener(TaskEvent.COMPLETE, doComplete);
taskSequence.start();

My task class invokes the load function in the issueProxy and also implements the IResponder interface to tell the task it is completed:
:
public class LoadIssuesTask extends Task implements IResponder
{
protected var issueProxy:IssueProxy;

public function LoadIssuesTask(issueProxy:IssueProxy)
{
this.issueProxy = issueProxy;
setCancelable(false);
setSkippable(false);
setSuspendable(false);
}

protected override function doStart () : void
{
    var token:AsyncToken = issueProxy.load();
    token.addResponder(this);
}

public function result(data:Object):void
{
complete();
}

public function fault(info:Object):void
{
}
}

Now all my http request are executed sequentially
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #22 on: April 15, 2008, 03:15:57 »

Nice! Extra points awarded for playing nice with other frameworks :)
Logged
Pages: 1 [2]
Print