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]
Print
Author Topic: AsyncCommand and remote proxies  (Read 17809 times)
willw
Full Member
***
Posts: 30


View Profile Email
« on: February 05, 2009, 10:40:02 »

I would like to execute an action on a remote proxy and get the result within the aegis of an AsyncCommand, so as to preserve context - 'This failure occurred while trying to execute a page up command' - without having extra bits of state lying around.

However, I am struggling. Don't see how to get the result of a remote call back to the command instance. Is it ok to use event listeners to communicate between proxy and command? Feels wrong.

Is this kind of thing an intended use of AsyncCommand? Have I got the wrong end of the stick?

Will


(PS: A tardy thanks to Cliff for his help with my previous Getting Started question.)
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #1 on: February 06, 2009, 06:54:46 »

When you invoke the Proxy method to make the remote call, pass a Function typed reference to the AsyncCommand's onComplete method into the Proxy method as an argument. Have the Proxy set this as a dynamic property such as 'callback' on the AsyncToken which is returned immediately from the remote call.  When the result comes back to the Proxy, store the data in the Proxy's data property and then call the callback function on the token property of the ResultEvent which will effectively complete the AsyncCommand.

(If you're using a delegate with the Proxy, you need to tunnel this function reference on through to it and set it on the token when the delegate makes the call.

However, that's not the only way to d what you're trying to do. Investigate the StateMachine utility.

You define your discrete application states, the StateMachine tracks them. You could go into the 'paging' state, trigger a call, and have the proxy failure be an action that triggers a state change to the 'error' state. On the transition into 'error' from 'paging' there are two notifications exiting and entering. On the exiting from 'paging' note, the destination state (error) is in the body (or type, sorry, I'm on my phone and can't remember which) of the notification. The command that is triggered by this 'exiting paging state, headed for error state' note can then display the  error message. And you could reuse that command if it is aware of all the states you might go to error from. And since it is the 'exiting' note, this command could be an AsyncCommand that pops a dialog and asks if you want to retry. If you do, it would send a StateMachine.CANCEL message, stopping the transition to error state and then trigger the command that makes the original call that failed again.

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


View Profile Email
« Reply #2 on: February 06, 2009, 11:00:09 »

> Have the Proxy set this as a dynamic property such as 'callback' on the AsyncToken

Brilliant! Didn't know about the AsyncToken. That is at least 19 times as clever as anything I had thought of. Possibly even 23 :-) And I can have the FaultEvent handler in the proxy do the same thing, and then my command always gets completed.

> Investigate the StateMachine utility.

I am sorely tempted. I would love to refactor the app as a state machine. State charts are my favourite kind of computer diagram - I find the act of drawing them clarifies my thoughts.

However, I need to progress the app in a quicker and dirtier way, and I am concerned by the prospect of needing to assimilate another tool, and redesigning what I have done to use it. I absolutely accept it would be much lovelier for the effort and a benefit in the long run. But.

I'll leave it alone for the weekend and see how I feel.

Thanks once again for your swift, detailed and most helpful answer.

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



View Profile WWW Email
« Reply #3 on: February 27, 2009, 10:53:26 »

A new version (1.1) of the StateMachine utility has been released for AS3 Standard & MultiCore.

Please read the release notes for details:
http://trac.puremvc.org/Utility_AS3_StateMachine/wiki/ReleaseNotes

@Will, you were absolutely right. Real-world use was seriously hampered if not downright impossible with the 1.0 implementation.

I considered all your input, but I wanted to keep this backward compatible while fixing the problem and not making it any more complicated in the process. Therefore suggestions about changing ACTION to TRIGGER and the like have been deferred.

The core issue I found was that the entering and exiting notes were not enough. The exiting note is useful for being able canceling a state change. The entering note is useful for kicking off any visual preparation for the new state. But if you tried to actually kick off another state transition in one of those notes, you couldn't because the machine hadn't actually changed its currentState yet. So evaluations about what state to go to were all mucked up.

At first I thought that use of AsyncCommand would be helpful in this but it turned out not to matter one whit. In fact with the new version of the statemachine, there's really no need for them, and simple commands are fine.

The first big missing link was a state-specific 'changed' notification. Don't kick off the actual business of a given state (such as calling a service, getting a return and then branching off to the related success or failure states) until this 'changed' note. Once the machine is IN a given state, then you can kick off any process, async or sync, leading to a StateMachine.ACTION note, and you'll be able to kick the machine reliably into the proper state from there.

There is still the StateMachine.CHANGED notification that goes out at the end, with a copy of the state and its name in the body and type. And I find that useful for aacross-the-machine-monitoring. For instance, I have various Mediators at the view which twiddle their view components in response to the StateMachine.CHANGED note. It's easier than having them for the state-specific notes, as you're just interested in one note. My view manipulations aren't necessarily one-on-one with the states. For instance I have several data entry screens /states associated with configuration of the app, but they each have their own state. The section header that says 'Configuration' needs to be present for several states. So I sign the mediator up for StateMachine.CHANGED, and in the handleNotification, several cases are stacked up and handled by the same code.

The other thing that was missing was the ability to pass data from one state to another. So now when you send a StateMachine.ACTION, the body can contain any object which will appear as the body of the subsequent 'exiting', 'entering', and 'changed' notifications. For instance in an ENTERING state, I may collect data, and then sent a StateMachine.ACTION note with the data collected in the body and the SAVING state name in the type. Then, in the command responding to the SAVING state's 'changed' notification, you take the data and save it, QED.

Upon reflection, you were also dead-on right about passing the state in the 'exiting', 'entering', and 'changed' notifications. There just wasn't any need for it, and you assumed that the recipient knew that this was a note triggered by exiting or entering a state. Pasing a data object was much more useful.

Anyway, I'm satisfied. I've got a non-trivial application running like a well oiled machine now. I will put out a more 'real-world' demo, soon, but I'm swamped at the moment. I'll probably be talking about the guts of this one in depth at FITC Toronto in April, though, if anyone here is attending.

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


View Profile Email
« Reply #4 on: March 03, 2009, 08:59:14 »

(Sorry for slow reply - I thought I was following this board, but wasn't.)

@Will, you were absolutely right.

TVM. The nicest thing one programmer can say to another ;-)

I'll have a look at your implementation. From what you say, it sounds much more to my taste.

I think there is still is slight mismatch between the way we think about it. I absolutely don't insist on my point of view, but I thought I'd write it out, so you are aware.

The way I have ended up thinking about it, is as follows:

The Exit note is good for things that must always happen on the way out of a state, no matter where you are going. Entry is similarly good for stuff on the way in. Whereas what I call the ACTION and (I think) you call the 'state-specific changed' note, the extra one that I wanted, allows one to carry out things associated with that specific transition.

For example, in my database editor there is an 'Editing' state.

  • On entry to this state (which can be by making a new record or editing an existing one - I don't care) the Entry action enables the Apply and Cancel buttons.
  • On exit from this state to anywhere (which can be by Applying or Cancelling the edit, or by doing something wild like hitting the logout button) I always want to disable the Apply and Cancel buttons, so this is handled in my Exit note.
  • On the Cancel button's ACTION/'transition-specific changed' note, and only on this particular Cancel transition, I want to call something that restores the record to how it was before. No other transition out of the 'editing' state will call this function.

One thing where we differ: I don't allow a cancel in the Exiting note. I feel by the time you start hitting Entry/Exit/Change notes, you are committed, and the state should change whatever happens.

Instead I want to handle the possibility of cancelling the transition in yet another, optional feature. This would be a GUARD, and it would be called before the Exit note. In effect it is a predicate: 'Do you want to do this state transition?'

So the sequence I envisage on a transition is

GUARD - fires tests that may abort the transition without leaving the old state
EXIT - stuff that always happens when you leave this old state
'Transition-specific note' [your jargon] / 'ACTION' [my jargon] - do specific stuff for this transition
ENTRY - welcome to the new state, wherever you have come from
CHANGED - (Cough) I don't see what this is for... How does it differ from ENTRY? Why two?

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



View Profile WWW Email
« Reply #5 on: March 04, 2009, 05:06:04 »

GUARD - fires tests that may abort the transition without leaving the old stateEXIT - stuff that always happens when you leave this old state'Transition-specific note' [your jargon] / 'ACTION' [my jargon] - do specific stuff for this transitionENTRY - welcome to the new state, wherever you have come fromCHANGED - (Cough) I don't see what this is for... How does it differ from ENTRY? Why two?W

GUARD isn't necessary. If you want to cancel a transition do it from a command triggered by the exiting note, by sending a StateMachine.CANCEL note.

StateMachine.CHANGED is broadcast at the end of every single state change and allows you to monitor the machine's behavior without signing up for every state-specific 'changed' note. Trust me you need both.

Use the entering note for a state to kick off visual transitions and register needed actors for that state if you like, but DON'T kick off anl logic or async process that might lead to another state change. Wait until the state-specific 'changed' note for that. Otherwise the machine will not have changed its  currentState property and will not act on the StateMachine.ACTION note generated during the execution of the 'entering'.note.

Hope this helps,
-=Cliff>
Logged
willw
Full Member
***
Posts: 30


View Profile Email
« Reply #6 on: March 04, 2009, 06:48:45 »

GUARD isn't necessary. If you want to cancel a transition do it from a command triggered by the exiting note, by sending a StateMachine.CANCEL note.

Yes, I understand (I think) your view of it.

Your view, as I understand it, is that to separate the notion of a test before the transition and the notification on exiting a transition is redundant, and I want to clutter your beautifully simple and elegant model with more baggage that achieves little.

In my view of the world, the fact that entry and exit seem symmetrical means they should be symmetrical in the way they work. Otherwise they contain a buried surprise. So my inclination is to separate the guard condition 'is this transition allowed to happen?' from the always-happen action 'we are now exiting the state'. I feel to mix or combine these two things is 'smelly'.

About right? :-)

FWIW I haven't implemented guards in my fork of the state machine code... (Cliff's voice: Aha!) but nor have I allowed transition aborts in the exit note handler. So far I have kept my conditional code inside the code that will fire the transition.

However, I do think I will need guards at some point.

StateMachine.CHANGED is broadcast at the end of every single state change and allows you to monitor the machine's behavior without signing up for every state-specific 'changed' note. Trust me you need both.

Honestly I don't need it, except, perhaps, for a debug monitor to show me what state the machine is in.

I write code that never queries the current state. The whole behaviour of the state machine is held inside the model, as described in XML. None of my mediators or commands 'know' what the current state is, nor tests it. Nothing. I regard this is a tenet of the way it is written. What happens when a trigger causes a transition is specified uniquely by that transition's action - in the XML. This is the only place where it is specified.

If I alter behaviour in the model, I know that I am not fighting any logic buried in the code in the mediators. I have achieved a clean separation between the structure of the app at top (state machine) level and the next level down (individual commands and mediators interacting with each other). Specific actions may be fired on state exit, entry and specific transition, but the code in the actions is unaware of the transition in which it is taking part. I feel this is in line with the coarse-grained, as-much-mutual-unawareness-as-possible philosophy that underlies PureMCV.

I think we may have drastically incompatible mental models of the way this is done. I am sure I am missing benefits and neat things permitted by your approach.

Never mind. People who agree about everything probably don't understand the issues ;-)

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



View Profile WWW Email
« Reply #7 on: March 05, 2009, 06:30:53 »

WRT performing guard logic in the exiting note I find almost no exiting notes at all in my FSM now. I don't see the need for the added complexity of another note specifically for guard, when it has a perfectly fine hook as it is.

As for 'never querying state' its not a query, its a notification. Just as all the players in an orchestra watch the conductor, I find there is no reason for the view not to organize itself around the StateMachine.CHANGE notes.

For instance, I have a SectionHeader that displays things like 'Configuration', 'Overview', 'Offline' etc. The headings sometims match the state one on one, and some times don't. For instance there are several states where the 'Configuration' header is used. This mediator is subscribed only to the StateMachine.CHANGED note, and knows which states to display 'Configuration' for. It only needs to subscribe to a single note. No tenants of OOP have been broken and I didn't have to define state-specific 'entering' notes for every state and subscribe to them.

-=Cliff>
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #8 on: March 05, 2009, 07:59:17 »

Sorry about that, looking back at the thread, I see I repeated myself about the SectionHeader mediator. A downfall of this silly WAP browser is I can't see the thread as I'm responding, making responses to long posts tough. I love my blackberry, but puuuhleeze won't someone write a decent browser for it?

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


View Profile Email
« Reply #9 on: March 05, 2009, 08:17:03 »

WRT performing guard logic in the exiting note [...]
I'd say your position is approximately as I stated... as remains mine. I am afraid we are going to have to agree to differ on this.

As for 'never querying state' its not a query, its a notification.
It's not whether it's a query or a notification that exercises me. It is the fact - as I possibly wrongly understand it - that your mediator code must sometimes need to 'know' about states, and test conditions resulting from the state of the state machine. My impression is you worry about whether a state transition has occurred or is in progress - and I loudly say you have no business so doing.

The tenet of OO that is broken is the encapsulation of the state machine itself.

To compare with another well-known state machine design. Consider the GoF State pattern. None of the classes that interact with the outer 'wrapper' class can know what the state is inside. The thing is sealed. From the point of view of an outside observer, all you can tell is that it seems to change its behaviour.

The point is: I believe that even as you have redesigned it, a user of this library is forced to break encapsulation. However, I claim this needn't be the case, but for a quite cheap change.

Perhaps I misunderstand what you are doing. If you can present a fragment of code for an example - eg a bit of the XML and the handling of the StateMachine.CHANGED note, then we could be sure we are on the same hymn sheet.

Will

PS: Found your follow up. Be grateful you have a BlackBerry. I have a Palm Treo. It would crash long before I could post to a thread like this :-)
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #10 on: March 07, 2009, 02:54:55 »

Don't confuse the GoF State pattern with the StateMachine utility, they are completely different things.

I will be posting some code, but I'm plowed under until FITC in April. The code I will be presenting will be very much StateMachine based, and demos will follow. If I have a chance I'll post something sooner, but I have described all the salient points in the release notes and forum posts.

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


View Profile Email
« Reply #11 on: March 09, 2009, 03:51:05 »

Don't confuse the GoF State pattern with the StateMachine utility, they are completely different things.

I am not confusing them. I did prefix my remarks with
To compare with another well-known state machine design.

My point it the GoF State pattern can be considered a model implementation of a state machine. And it has a very desirable attribute - encapsulation of its actual state - that yours doesn't have.

You asserted that your implementation doesn't break any tenets of OOP. I am saying it jolly well does. I am trying very hard to get you to look again at an important design defect in the utility.

AIUI your design forces the user to test state outside the state machine. The problem with this is that you are build a dependency between the state machine itself - specifically the XML fragment that describes it - and the 'outside' code in the rest of your application. So that if you change the design of the state machine - for example splitting one current state into two - you will need to chase up all references to the state machine's state in the code that interfaces to it. To borrow and despoil your analogy, bits of the conductor have leaked out into the orchestra.

My assertion is that, as a matter of good, decoupled design, the mediators and commands that are driven by the state machine must be unaware of how the 'conductor' does his work.

What I propose is: when a given transition occurs, you optionally send out a 'effect' notification, which (probably) fires a command that performs the actions required by this transition. This is akin to the notifications sent out on entrance and exit of states, but it is tied to the transition, not to the states. Note that you can choose to have different transitions issue the same command.

Perhaps a concrete example will help. Here is a teeny bit of state machine stolen from something posted into the forum elsewhere:

:
<state name="TOP_LEFT">
<transition action="TO_TOP_RIGHT" target="TOP_RIGHT" />
</state>

<state name="TOP_RIGHT">
<transition action="TO_BOTTOM_RIGHT" target="BOTTOM_RIGHT" >
</state>
//...
It models a box that moves between the corners of the screen, but only horizontally and vertically in a clockwise direction.

To interface to this code, one is going to have to write a handler in a mediator like this (I have omitted 'ApplicationFacade.' in front of string constants throughout this stuff)
:
// In a Mediator somewhere
override public function handleNotification(note:INotification):void
{
switch (note.getName()) {
case StateMachine.CHANGED:
// Have to test the FSM's current state
if ((note.getType() == TOP_RIGHT) {
viewComponent.doMoveRight()
} else if ((note.getType() == BOTTOM_RIGHT) {
viewComponent.doMoveDown()
//...

Notice that the mediator's code 'knows' about the states TOP_RIGHT, BOTTOM_RIGHT etc, and it also 'knows' about the behaviour of the state machine. It knows that if the state machine is in the TOP_RIGHT position, it must have got there from TOP_LEFT, so doMoveRight() should be called.

Here is the FSM fragment with my proposed variation - the addition of an 'effect' attribute:
:
// FSM
<state name="TOP_LEFT">
<transition action="TO_TOP_RIGHT" target="TOP_RIGHT" effect=CMD_MOVE_RIGHT/>
</state>

<state name="TOP_RIGHT">
<transition action="TO_BOTTOM_RIGHT" target="BOTTOM_RIGHT" effect=CMD_MOVE_DOWN>
</state>
//...
And here is the code in the mediator
:
override public function handleNotification(note:INotification):void
{
switch (note.getName()) {
case CMD_MOVE_RIGHT:
// Don't care about the FSM's current state
viewComponent.doMoveRight();
break;
case CMD_MOVE_DOWN:
viewComponent.doMoveDown();
break;
//...

I hope you will agree that this is already better code. There is no need to add logic that queries the FSM's state - it simply obeys incoming commands.

So far, so good.

Now suppose that the spec changes (as I hear sometimes happens), and it is desired that the box can move clockwise and anti-clockwise. In the current utility, the state machine fragment changes like this:

:
<state name="TOP_LEFT">
<transition action="TO_TOP_RIGHT" target="TOP_RIGHT" />
<transition action="TO_BOTTOM_LEFT" target="BOTTOM_LEFT" />
</state>
//...

Notice how the code to support this changes:
:
// In a Mediator somewhere
override public function handleNotification(note:INotification):void
{
switch (note.getName()) {
case StateMachine.CHANGED:
// Have to test the FSM's current state
if ((note.getType() == TOP_RIGHT) &&
viewComponent.currentPos() == TOP_LEFT) {
viewComponent.doMoveRight();
} else ((note.getType() == TOP_RIGHT) &&
viewComponent.currentPos() == BOTTOM_RIGHT) {
viewComponent.doMoveUp();
} else // Tons more modified code here...
//...

We have to query not only the current state of the state machine but, in effect, the previous state too, which I guess will be cached in the component. (In real life, this logic should maybe be put into the component - cut me some slack on that detail). Anyway, we have to make tons of changes to accomodate the change ion the state machine.

Now my proposed altered design. Its FSM is very similar:

:
<state name="TOP_LEFT">
<transition action="TO_TOP_RIGHT" target="TOP_RIGHT" effect=CMD_MOVE_RIGHT/>
<transition action="TO_BOTTOM_LEFT" target="BOTTOM_LEFT" effect=CMD_MOVE_DOWN />
</state>
//...
And here is the code in the mediator
:
override public function handleNotification(note:INotification):void
{
switch (note.getName()) {
case CMD_MOVE_RIGHT:
// Don't care about the FSM's current state
viewComponent.doMoveRight();
break;
case CMD_MOVE_DOWN:
viewComponent.doMoveDown();
break;
//...
Yup, it's exactly the same as before. Not a line changed.

With my proposal, when you alter your state machine's internals, you do not have to change any code outside it. Nothing outside the state machine tests for any conditions, so no code beyond the XML is affected. The constants defining state names can be defined in local scope of the FSM injecting routine.

It's neat, and it works, and it is not a big change to what you have. So how about it?

I apologise for harping on. If I have misdescribed how your system works, then please put me right. But it is quite a difference.

Will

PS: 'effect' notification is what is called an 'action' in standard FSM terminology. But I don't want to go around that loop again.


Logged
Pages: [1]
Print