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] 3 4
Print
Author Topic: AsyncCommand - A PureMVC / AS3 Utility  (Read 76612 times)
Tekool
Sr. Member
****
Posts: 192


View Profile WWW Email
« Reply #15 on: March 11, 2009, 06:06:22 »

I do not know what Cliff will say, but now your request seems fair to me. Sorry not to have understood immediately.  :-[
Logged
Ondina
Sr. Member
****
Posts: 86


View Profile Email
« Reply #16 on: March 11, 2009, 06:46:50 »

In my app I have lots of comboboxes as a part of a form, that need to be populated on the creationComplete of the view.

I have only one proxy  for retrieving the combos' data from the server.

Instead of having an AsyncCommand for each combo I (mis)used an AsyncCommand that calls itself 
( in the callBack function of the asynccommand where I have the commandComplete())
 until all combos have data.

I used an AsyncMacroCommand to call the AsyncCommand because when I called the AsyncCommand alone the debugger said that onComplete was null. I didn't know the meaning of that error.
So I thought I misused the AsyncCommand.

After seeing this post I made the changes suggested by Will and now the AsyncCommand works fine.
Logged

~ Ondina ~
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #17 on: March 13, 2009, 07:06:37 »

My impression of AsycCommand is that it doesn't make any since to use it outside the context of an AsyncMacroCommand. The macro is looping until the children report completion. The only difference between a SimpleCommand and an AsyncCommand is that the latter is capable of storing and invoking the callback when done. What reason would one Have for using it outside that context?

-=Cliff>
Logged
Ondina
Sr. Member
****
Posts: 86


View Profile Email
« Reply #18 on: March 13, 2009, 08:52:51 »

Hi Cliff

Yeah, you are for sure right, I'm using the AsyncCommand outside of  its scope, but how would you solve this issue:

I have a ViewStack with let's say 8 Views.
Each View contains a form.
Many of those forms contain several ComboBoxes with their dataProvider coming from database tables.
When the View is created the ComboBoxes should get their data.
So one ComboBox after another receives the dataProvider from server.

If for some reason the remote call failed, I have to try to reload the data.
For that to happen I have to let the Mediator know that the data for a certain ComboBox is missing and the View should enable a Button that would let the user call the server again for this particular ComboBox.

Instead of having an AsyncMacroCommand with 3 AsyncCommands ( AsyncCombo1, AsyncCombo2, AsyncCombo3 )  for the first View and then another AsyncMacroCommand with 5 AsyncCommands (  AsyncCombo4, AsyncCombo5 and so on ) for another View, I made ONE AsyncCommand for all the Combos in all my Views.

The Mediator of each View calls this ComboAsyncCommand passing it an array of the combos that need data in the body of the Notification.

The AsyncCommand  retrieves the only one proxy ( ComboProxy ).
The ComboProxy  reads the needed info ( for example the name of the database table ) from a  ComboVO ( common to all the combos ).

When data has arrived the ComboPoxy sends a notification for the Mediators ( the combo gets its data ) and calls the  callBack function in the ComboAsyncCommand , that is then ready for the next Proxy call.

If for some reason I have to change or rearrange  the ComboBoxes in my views, I only need to tell the View's Mediator the new ID of the Combobox, which is the same name as the database table.

I (mis)used the AsyncCommand because it is already there with the ability of getting and sending Notifications and its onComplete method. 
In the callBack method of the Command I can send a Notification for the Mediator in case of an rpc FaultEvent, but I can continue with the  calls for the rest of the ComboBoxes.

I can as well call the ComboAsyncCommand in an AsyncMacroCommand together with another AsynCommand that would call a Proxy for a DataGrid if the View needed both.

Before making the changes suggested by Will, I had an AsyncMacroCommand calling a single AsyncCommand over and over again until all Combos had data.

Oh... I hope my explanation isn't too convoluted and you'll understand what I meant.
Maybe I really don't need an AsynCommand, but I didn't know how else to do it.
I would really appreciate it, if you showed me a better solution for this kind of problem.

Thanks, Ondina
Logged

~ Ondina ~
willw
Full Member
***
Posts: 30


View Profile Email
« Reply #19 on: March 13, 2009, 09:30:28 »

As I said to Tek, surely the point of having AsyncCommands is that they can be compounded into macros or not, as the need arises.

The current code prevents this. A correctly-written AsyncCommand cannot be used by itself. There is no indication in the docs to suggest that this is intentional.

The proposed correction breaks no code AFAICS. It seems uncontroversial to me.

Will


Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #20 on: March 13, 2009, 08:43:08 »

Ok, maybe I'm missing the point. How does a standalone AsyncCommand differ from a SimpleCommand in such a way that would cause you to use it as anything other than a subcommand of an AsyncMacroCommand?

@Ondina, I tried to follow your explaination, but you said that you passed the array oc combos to your ComboAsyncCommand, it populates them and then calls its commandComplete method? That's where I lose track. Will's suggestion jus plugs the hole of trying to invoke the callback if it isn't there. Which it isn't. So it doesn't do anything. I.e. Isn't any different from a SimpleCommand doing its work then ending.

Its late so I may have missed something crucial, but otherwise, substitute a SimpleCommand and see if it doesn't work the same.

-=Cliff>
Logged
Ondina
Sr. Member
****
Posts: 86


View Profile Email
« Reply #21 on: March 14, 2009, 04:26:25 »

Hey Cliff

OK. Forget about that array.

Let's start by the definition:

"The AsyncCommand utility offers a solution to the problem of executing a series of commands each of which may need to complete one or more asynchronous operations before the next command is executed."

I might have misunderstood the idea of the AsyncCommand, but isn't it that commandComplete();   that lets the AsyncMacroCommand know when to call the next command?

Of course I can use a SimpleCommand to loop a service call. It works just fine if I used it alone.

But if I need the same (Simple) command in an AsyncMacroCommand  the “next” command is called simultaneously and not after the SimpleCommand has completed.

1. So let's say in an AsyncMacroCommand I add the subcommands:

SimpleCommandOne (without any looping , without any arrays,  just a simple proxy call)
SomeOtherAsyncCommand
SimpleCommandTwo
AnotherAsyncCommand

Here I can't control in which order they would execute.

2. But if I had

SimpleAsyncCommandOne (without any looping , without any arrays,  just a simple proxy call)
SomeOtherAsyncCommand
SimpleAsyncCommandTwo
AnotherAsyncCommand

everything works as it should.

The problem is that I  need the SimpleAsyncCommandOne somewhere else in my code to be called outside of an AsyncMacroCommand and I can't do that without that hack:

protected function commandComplete () : void
{
   if (onComplete != null)
   onComplete();
}

--
The more real scenario :

View1's mediator
ComboAsyncCommand called standalone . ( for 3 ComboBoxes)

View2's mediator:
An AsyncMacroCommand calls
ComboAsyncCommand ( for 1 ComboBox)
DataGridAsyncCommand (for 1 DataGrid)
ListAsyncCommand ( 1List)
AnotherAsyncCommand ( for some other reason)

View3's mediator:
DataGridAsyncCommand standalone (for 2 DataGrids)

View4's mediator:
An AsyncMacroCommand calls
ListAsyncCommand (2 Lists)
ComboAsyncCommand ( for 4 ComboBoxes)
--

I still would like to know how to do it better.

So I don't want to prove anything, I'm just trying to find my way through the puremvc labyrinth.
As I said I might be the one confusing things and if it is so, please  excuse my newbiness  and my not so good explanations :)

Ondina
Logged

~ Ondina ~
willw
Full Member
***
Posts: 30


View Profile Email
« Reply #22 on: March 14, 2009, 09:11:03 »

How does a standalone AsyncCommand differ from a SimpleCommand in such a way that would cause you to use it as anything other than a subcommand of an AsyncMacroCommand?

Look at it the other way round. If you have an AsyncCommand that, you realise, can be useful standalone, why should you have to duplicate its code as a SimpleCommand to use it?

or

If you design a command that is intrinsically asynchronous (eg it makes a round trip call to the server), why wouldn't you design it as an AsyncCommand rather than a SimpleCommand, and thereby reserve the opportunity to compound it into an AsyncMacroCommand if the need arises?

We seem to be making heavy weather of this. The suggested change is very cheap, and is invisible to those who don't care... but allows an expected use to work for those who do. I feel I have already written more than enough to justify it. If you don't want it, well, fair enough.

Will
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #23 on: March 15, 2009, 08:31:15 »

The simplicity of change isn't the point. The point is the AsycCommand is absolutely useless in any context other than as a subcommand of an AsyncMacroCommand. Its call for completion is meaningless.

And the danger is that someone thinks that the thread of execution has stopped during the asych activity, which is not the case unless it is a sub of an AsyncMacroCommand. In any other setting, the thread of execution keeps right on truckin'.
 
Btw, I am not the author of this utility, that is Duncan Hall. You may wish to take up the question with him.

-=Cliff>
Logged
willw
Full Member
***
Posts: 30


View Profile Email
« Reply #24 on: March 16, 2009, 12:37:23 »

The point is the AsycCommand is absolutely useless in any context other than as a subcommand of an AsyncMacroCommand. Its call for completion is meaningless.
You keep saying this. It just isn't the case.

An AsyncCommand 'has meaning' for example if you wrap it around a remote call. When the remote call returns, it can take appropriate action (handling failure, notifying  other participants), and finally call commandComplete().

Why would I only run the above in the context of an AsyncMacroCommand?

And the danger is that someone thinks that the thread of execution has stopped during the asych activity, which is not the case unless it is a sub of an AsyncMacroCommand. In any other setting, the thread of execution keeps right on truckin'.
Not true.

If you write asynchronous code, the thread continues executing irrespective of AsyncMacroCommand's involvment. All AsyncMacroCommand does is get control back at some point in the future and pass it to the next command.
 
Btw, I am not the author of this utility, that is Duncan Hall. You may wish to take up the question with him.

Ok, if that's the right thing to do. How do I contact him?

Will
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #25 on: March 16, 2009, 03:07:51 »

So, when you finally call commandComplete on an AsyncCommand, what exactly is it that you wish to happen? If it is not a subcommand of an AsyncMacroCommand, its like the sound of one hand clapping. It signifies nothing. No one is listening.

As for the thread of execution, if an AsyncMacroCommand is running, the thread is running, but in a loop in the AsyncMacro which is waiting for the subcommand to report completion. If you don't have the macro running then your thread moves on. So someone might think well I can just use the AsyncCommand and further processing will be suspended until I call commandComplete, which actually does NOTHING at all without the AsyncMacroCommand.

The project page for each utility has a link to the author's page, which has their email address, so if you'd like to talk to Duncan about making this change, just send him an email.

BTW, I agree with the change, I just don't agree that the AsyncCommand has any use whatsoever outside the context of its attending AsyncMacroCommand and want anyone reading this thread to understand why. Personally, I rarely do service interaction in commands (Cairngorm style). Since most service interactions end up pulling data into the client that will be used for display and or update back to the server, a long lived entity is more appropriate, which is why the best practices doc suggests use of proxy or proxy/delegate as the preferred method of handling services.

Recently I have found a few service use cases that makes sense to handle in a command; checking for internet connectivity before proceeding with startup in an AIR app that requires net connectivity to do its job, then checking that the user has a valid licens, then checking that the user has an S3 account. In this sequence, I looes at AsyncCommand and several other approaches, and in the end I found that the combination of the StateMachine with SimpleCommands was the easiest answer. The states of 'checking internet', 'checking license', 'checking S3' kick off SimpleCommands with their state-specific 'changed' notes, and sinc the machine is sitting in a given state and is only kicked out of it and into the next state when a given StateMachine.ACTION note is sent, its easy to have the command make the call get the result and send a result or failure notice. The StateMachine then moves to the next state (or a tangential state such as 'offline' or entering license' or 'entering S3 credentials'. Of course, after entering license info, I move to a 'saving license' state then back to the 'checking license' state in a loop until license check passes, then on to 'checking s3' where a similar loop happens if credendials haven't been entered and saved.

Handling that sort of multistep process where each step could fail and needs to be addressed before the train moves to the next stop a big ugly bear that just crawled from a foul smelling swamp if you approach it with a linear 'chained command' approach. If you're going that route then the StartupManager utility is far more appropriate because the failure recovery options have been addressed in a very elegant way.

-=Cliff>
Logged
Ondina
Sr. Member
****
Posts: 86


View Profile Email
« Reply #26 on: March 17, 2009, 02:30:46 »

Hi Cliff

If it is not a subcommand of an AsyncMacroCommand, its like the sound of one hand clapping. It signifies nothing. No one is listening.

Yea pure Zen :)

the AsyncCommand and further processing will be suspended until I call commandComplete, which actually does NOTHING at all without the AsyncMacroCommand.

That's true. The commandComplete  does nothing when it is used in a standalone AsyncCommand.
An AsynCommand called outside an AsyncMacroCommand behaves like a SimpleCommand and that's the way I used it, as a SimpleCommand in one place and as an AsyncCommand in another context.

So if I wanted to go by the rules I should have written 2 Commands , one SimpleCommand and one AsyncCommand having the same code inside, of course with  the SimpleCommand not having the commandComplete .  It wouldn't be a big deal to duplicate that code for a (one) situation like this. But my application is huge, I have many similar situations and if I wouldn't reuse the code  I would have lots of Commands that would behave against the DRY principle.


Personally, I rarely do service interaction in commands (Cairngorm style). Since most service interactions end up pulling data into the client that will be used for display and or update back to the server, a long lived entity is more appropriate, which is why the best practices doc suggests use of proxy or proxy/delegate as the preferred method of handling services.

Well now I'm really confused. I'm not sure which part of your post is addressed to me and which to Will or if it is just a general observation.


I don't make the service call in the commands. I retrieve the proxies there and call their methods.
Is this a bad way to do it?

In my code the Proxy is handling the services.
I also have a “RemoteDelegate” called by different Proxies with different endpoints and different requests.
The route is like this:

View->Mediator->Command->Proxy->Delegate
Delegate->Proxy->Mediator->View

The View needs data, the Mediator calls the Command, the Command retrieves the Proxy, the Proxy gets the service result from a Delegate and sends a Notification to the Mediator, which takes the note.getBody() object and populates the View's ComboBox with that object.
Of course if I showed you the code it would be easier to understand what I'm talking about, but    since you are on your BlackBerry you wouldn't see it anyway.


Handling that sort of multistep process where each step could fail and needs to be addressed before the train moves to the next stop a big ugly bear that just crawled from a foul smelling swamp if you approach it with a linear 'chained command' approach.

So now where should I make the loop for the service call, if not in the command?

A.Should I do it in the Mediator?
  1.Calling a SimpleCommand repeatedly
  2.calling a Proxy repeatedly (meaning same code in each Mediator that needs data from server)
B. Should I call the Proxy in a Command  repeatedly?
C.Should I loop the service call in the Proxy itself?

You mention the  StartupManager.
Are you suggesting that I should use the  StartupManager for each View that has ComboBoxes that need to be populated with data from the server?
In the  StartupManager example there are 3 different Proxies for 3 different sets of data.
I have one Proxy for similar data ( ComboBoxes dataProvider).
I don't quite understand  right now how I can use it.
Hopefully I will be able to find a solution without bothering you again:)
I'm sorry for the inconveniences I caused in here.

Ondina
Logged

~ Ondina ~
willw
Full Member
***
Posts: 30


View Profile Email
« Reply #27 on: March 17, 2009, 03:21:53 »

So, when you finally call commandComplete on an AsyncCommand, what exactly is it that you wish to happen? If it is not a subcommand of an AsyncMacroCommand, its like the sound of one hand clapping. It signifies nothing. No one is listening.
I don't understand why we are struggling with this so much, because it really, really isn't very hard. I must be putting my point over really badly.

Let's break it into steps, and see if we can't identify the point where we diverge

1. AsyncCommand is a convenient wrapper for any process that completes asynchronously. Ok? You may not approve of its use, but that's how I use it, in fact using a design of your suggestion - which works very well for me. See first para here:
When you invoke the Proxy method to make the remote call, pass a Function typed reference[...]

2. When the remote call returns control to the proxy, it uses a callback to raise the second part of the AsyncCommand, just as suggested in your design.

3. The second part of AsyncCommand completes its business. Typically it emits a notification.

4. Finally, because this AsyncCommand doesn't know whether or not it is part of a macro, it must call commandComplete(). I repeat: it does not know whether it is a subcommand or standalone. So the whole business of 'one hand clapping' does not arise. It doesn't care whether it has a parent or not; it just knows that it must notify a parent if it happens to exist. So commandComplete() mustn't barf if it happens not to have a parent. Because it has to call commandComplete() anyway. Because it doesn't know. Pant, pant, pant ;-)

As for the thread of execution, if an AsyncMacroCommand is running, the thread is running, but in a loop in the AsyncMacro which is waiting for the subcommand to report completion. If you don't have the macro running then your thread moves on. So someone might think well I can just use the AsyncCommand and further processing will be suspended until I call commandComplete, which actually does NOTHING at all without the AsyncMacroCommand.
Sorry, but this is quite wrong. But we needn't have this as an abstract argument.

Here is the code from http://puremvc.org/pages/utils/AS3/Utility_AS3_AsyncCommand/srcview/; this is the key function from AsyncMacroCommand.as:
:
        private function nextCommand () : void
        {
            if (subCommands.length > 0)
            {
                var commandClassRef:Class    =    subCommands.shift();
                var commandInstance:Object    =    new commandClassRef();
                var isAsync:Boolean            =    commandInstance is IAsyncCommand;
               
                if (isAsync) IAsyncCommand( commandInstance ).setOnComplete( nextCommand );
                ICommand( commandInstance ).execute( note );
                if (!isAsync) nextCommand();
            }
            else
            {
                if(onComplete != null) onComplete();
               
                note         =    null;
                onComplete    =    null;
            }
        }                     
The key thing to note is that, if executing an asynchronous command, it drops out of the function. It is not in a loop! It couldn't be, otherwise nothing else could happen within the main Flash thread.
(Whereas with a simple command, it does indeed loop around, calling itself recursively. This is what this line is about:
:
                if (!isAsync) nextCommand();
but you notice it specifically excludes asynchronous commands.)

So anything can happen when an AsyncCommand or an AsyncMacroCommand is executing, and there is no difference between the two in terms of thread loops. They are both waiting for a callback.

Now (I hope) I have cleared that up for you, perhaps we are nearer to seeing eye to eye regarding how AsyncCommand works.

The other thing that is amusing to notice is that this code already contains exactly the test that I propose for AsyncCommand
:
                if(onComplete != null) onComplete();
This test is there for exactly the same reason that I suggest it: AsyncMacroCommand in turn may (or may not) be aggregated into further AsyncMacroCommands - but the code in each level doesn't 'know' whether this has happened or not.

The project page for each utility has a link to the author's page, which has their email address, so if you'd like to talk to Duncan about making this change, just send him an email.
Thanks - will investigate.

Since most service interactions end up pulling data into the client that will be used for display and or update back to the server, a long lived entity is more appropriate, which is why the best practices doc suggests use of proxy or proxy/delegate as the preferred method of handling services.
I use a proxy to handle services - as per the recommendation. I also call proxy functions from the commands. By making the command asynchronous, I found I could contain the whole business of handling the callback within the context of the command.

I tried modelling the system with state changes - ie the arrival of the data in the proxy generated a notification, which caused a state change. This worked, but I like the localisation of the service call that is provided by the AsyncCommand. By handling remote calls in a single command, nothing else has to 'handle' them.

By the way, I still use the proxies to do all the data handling. In effect, what I have done is replace the notification emitted by the proxy in classic pureMVC design by a callback to the owning AsyncCommand. This means I don't have to declare any notification string constants in proxies, and so don't have to reference the proxies in any mediators (because the notifications are emitted by the commands). So I break completely the dependency of the mediator on the proxy - it's another gain for separation of concern.

However, as you know, I am absolutely an enthusiast for state machines, and definitely advocate their use in these types of applications. There are no swamp-smelling bears in my app! :-)

Will
Logged
Gallo_Teo
Newbie
*
Posts: 8


View Profile Email
« Reply #28 on: March 17, 2009, 09:48:15 »

hi everybody, with the scope tu always use async command instead of simpleCOmmand i modified this simply line in asyncCommand class

protected function commandComplete () : void
      {
         if ( onComplete != null )
            onComplete();
      }

hope helpful

Teo
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #29 on: March 17, 2009, 03:37:56 »

@willw, Aha! Thanks for sticking through this with me. I had an imperfect understanding of the AsyncMacroCommand. When I originally looked at it I misinterpreted the "if (!isAsync) nextCommand();" as a loop. I see now it clearly lets the thread keep on going. Which makes me scratch my head all the more on the use of the entire utility. I get that it causes AsyncMacroCommand (herinafter refered to as the macdaddy) not to execute the next subcommand until the current one completes, but if it's not blocking, (i.e. holding the thread in a loop until completion of the subcommand) then I can't imagine ever using it, period. Why?

Lets say I've got 3 AsyncCommands that I want executed before moving to the next state. They all fetch data that is needed by that destination state and they need to happen in order because one uses the results of the previous to make its call. But while one of them is off waiting, the user does something that causes a transition to another state wherein I don't want that data I was in the process of fetching. So the StateMachine moves to the new state, but the Macdaddy is still in memory as well as the waiting subcommand, which now gets its data, calls commandComplete, and then its Macdaddy now happily moves on to execute the next (now unwanted) subcommand which goes and fetches a huge gob of useless data.

Based on this understanding, for my part, I'll never use or advocate IAsyncCommands if the AsyncMacro is non-blocking. You can do the same thing in a SimpleCommand as an AsyncCommand, and the StateMachine can guarantee you aren't going to run into the problem described above. Use it if you want, but just be aware of that scenario and the limitation of the utility.

Myself, I'd prefer to identify each of those processes as discrete states. That gives you much more control over the result/fault branching that needs to be handled for each call. And the entire process is definable in XML in the FSM definition. Once you begin defining your app as a StateMachine all this tedious coordination of chained commands (and the lack of blocking and how to tell the remaining ones that they don't need to run because the whole process they were a part of has been canceled) goes away.

-=Cliff>
Logged
Pages: 1 [2] 3 4
Print