Announcement Announcement Module
Collapse
No announcement yet.
question about an assumption behind Operations Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • question about an assumption behind Operations

    Hello.

    I was excited to download and play with Spring Actionscript 1.0. I like the improvements, and I LOVE having documentation to help me better understand the Operation/Command Framework. Good work.

    I wanted to ask for your reasoning behind one design decision - and my question might be naive as I'm relatively new to Flex.

    It appears that Operation constructors must NOT return their results immediately. Here's GenericOperationCommand.execute():
    Code:
    			_operation = ClassUtils.newInstance(_operationClass,_constructorArguments);
    			_operation.addCompleteListener(operationComplete);
    			_operation.addErrorListener(operationError);
    If the operation's constructor immediately (i.e., synchronously) dispatches a completion or error event, the listeners will not receive this event.

    I've thought of a couple of reasons why I might want operations to return immediately:
    • Testing. I wanted to create a MockOperation that failed consistently, 100% of the time. But my MockErrorOperation, wrapped in a GenericOperationCommand, didn't work because it dispatched its error event before listeners were attached. I could defer the result using a timer, but I don't see why I'd want to.
    • Caching. I could see myself creating a service-layer cache, and, again, see no reason to defer the results.

    Your docs explicitly state that Operations execute asynchronously, and so I wanted to hear more about your reasons not to allow synchronous operations. In my testing and caching cases above, am I trying to do something foolish?

    Again, thanks for the good work!

    Phil

  • #2
    operations

    Hey Phil,

    Operations are always asynchronous because they are an abstraction for all the different async tasks that are available in the Flashplayer/Flex environment. Loading of modules, calling remote objects, etc. Therefore it doesn't make sense to also allow synchronous operations, it would defeat the initial purpose of the abstraction.
    As for your examples of synchronous operations, I would not recommend such an approach because the same class would exhibit different behaviour depending on certain circumstances. If you need to unittest a class that uses operations, in the 'real world' its execution would be asynchronous (i.e. it calls a remote object), but in your test situation it would be synchronous. In other words, you're not testing the proper situation. I'm afraid a mock operation would indeed need to simulate a remote call by using a timer.
    You can check out this class for a simple example:
    MockOperation

    As for the caching, I think you'd need to shift the caching check to a different point in your business logic. For instance, if you use a command that retrieves certain data using an IOperation you could perform the cache check inside the execution logic of the command. If there needs to be a data refresh you create the operation, if the data is still valid, just return the previously retrieved content.

    I hope I'm making sense here. If you have any more questions or comments we'd love to hear about them.

    cheers,

    Roland
    Last edited by 666shooter; Apr 4th, 2010, 02:37 PM. Reason: fixed typo's

    Comment


    • #3
      Originally posted by 666shooter View Post
      Hey Phil,

      Operations are always asynchronous because they are an abstraction for all the different async tasks that are available in the Flashplayer/Flex environment. Loading of modules, calling remote objects, etc. Therefore it doesn't make sense to also allow synchronous operations, it would defeat the initial purpose of the abstraction.
      As for your examples of synchronous operations, I would not recommend such an approach because the same class would exhibit different behaviour depending on certain circumstances.
      Hi Roland.

      Thanks for your reply. It makes sense.

      I guess my underlying question was, "Does it make sense to define an abstraction that can sometimes behave synchronously and sometimes asynchronously?"

      It sounds like your answer is no...that whether something is synchronous or asynchronous is fundamental to the abstraction itself and should be called out.

      And that makes sense to me. I can see how knowing that an API is asynchronous allows me to reason better about the API and to make some simplifying assumptions (likewise with knowing that the API is synchronous).

      Phil

      Comment


      • #4
        yez

        Hey Phil,

        Yep, that's about the gist of it I'd say. For synchronous behaviour there's always the ICommand interface. Its execute() method returns the result of the implementation, if you use the IASyncCommand interface you'll need to hook up event listeners since it's result is retrieved asynchronously.
        Of course, in that case you can check if execute() returns null, in that case the behaviour is async, if it returns a non-null value, it was synchronous.
        That way you can create the sync/async behaviour for yourself should it makes sense for your particular situation.

        cheers,

        Roland

        Comment


        • #5
          I must admit I had exactly the same reaction as Phil when i discovered the operations API.
          I expected operations to work the same way with sync and async code while it was not the case.
          I wanted my service in my app to return IOperation objects and be able to listen to operation results.
          It worked until I noticed problems with loading modules.
          (ModuleManager load() methods returns (dispatches events) immediately if a module has already been loaded hence my Service operation listeners were not executed every time)

          So I had to refactor my service to return an IAsyncCommand rather than an IOperation (to have time to register listeners before the actual operation starts).

          So, my recommendation would be to use operations ONLY when you are absolutely sure the operation implementation will indeed be asynchronous. For all the other cases, I'd rather use an IAsyncCommand which looks more 'safe' to me.

          Comment


          • #6
            IAsyncCommand feels safer

            Ozeebee, I also refactored my services to use IAsyncCommands instead of IOperations because I preferred not to make assumptions about when my API's clients would register listeners.

            Did you write your own IAsyncCommand implementation to handle module loading? GenericOperationCommand wouldn't do what you'd expect - at least I don't think it would:

            Code:
            		public function execute():* {
            			_operation = ClassUtils.newInstance(_operationClass,_constructorArguments);
            			_operation.addCompleteListener(operationComplete);
            			_operation.addErrorListener(operationError);
            			if (_operation is IProgressOperation){
            				(_operation as IProgressOperation).addProgressListener(operationProgress);
            			}
            		}

            Comment


            • #7
              You're right, I don't trust GenericOperationCommand either

              I have actually rewritten the LoadModuleOperation class to make it implement the IAsyncCommand interface.

              It looks like this :
              Code:
              	
              	public class LoadModuleOperation extends AbstractProgressOperation implements IAsyncCommand {
              		protected var moduleInfo:IModuleInfo;
              		
              		private var applicationDomain:ApplicationDomain;
              		private var securityDomain:SecurityDomain;
              		private var moduleFactory:IFlexModuleFactory;
              		
              		public function LoadModuleOperation(moduleURL:String, applicationDomain:ApplicationDomain=null, securityDomain:SecurityDomain=null, moduleFactory:IFlexModuleFactory=null) {
              			Assert.hasText(moduleURL, "The moduleURL argument must not be null or empty");
              			super();
              			init(moduleURL, applicationDomain, securityDomain, moduleFactory);
              		}
              		
              		protected function init(moduleURL:String, applicationDomain:ApplicationDomain, securityDomain:SecurityDomain, moduleFactory:IFlexModuleFactory):void {
              			this.applicationDomain = applicationDomain;
              			this.securityDomain = securityDomain;
              			this.moduleFactory = moduleFactory;
              			moduleInfo = ModuleManager.getModule(moduleURL);
              			moduleInfo.addEventListener(ModuleEvent.READY, readyHandler, false, 0, true);
              			moduleInfo.addEventListener(ModuleEvent.ERROR, errorHandler, false, 0, true);
              			moduleInfo.addEventListener(ModuleEvent.PROGRESS, progressHandler, false, 0, true);
              		}
              		
              		public function execute():* {
              			moduleInfo.load(applicationDomain, securityDomain, null, moduleFactory);
              		}		
              	
              Of course, now I have to call the execute() method to actually run the operation
              But at least I'm sure the listeners are triggered whatever the behaviour of the underlying implementation.

              Comment


              • #8
                hmm

                Hey guys,

                I'm still a bit puzzled. I guess I'm going to have to put a bigger disclaimer on the Operations documentation page.
                If you read the introduction:
                Spring Actionscript - Operations API

                Don't these first paragraphs clearly state that operations deal with asynchronous functionality? So, when you state:
                I expected operations to work the same way with sync and async code
                to me, this doesn't make sense, because operations should not be in any synchronous code.

                What you consider a workaround (or so it kind of sounds to me here), i.e. using an IAsyncCommand is actually the right way of going about this. This is because you can add the code that checks whether the async operation is necessary in the command implementation, which would be the right separation of concerns.

                If you REALLY want to stick this in an operation's implementation then the only way to go about this would be to pass in your event handlers as constructor arguments. That way you can handle the event listening yourself in your initialisation code. But again, I recommend against this since it feels really hacky to me

                Its interesting to read your thoughts though, I will give it some thought on how to elaborate the documentation a little to prevent future confusion for any other users. So thanks for posting your comments here, its always helpful.

                cheers,

                Roland

                Comment


                • #9
                  Hi Roland,
                  I understand you opinion and I like the abstraction but in practice people will make the same mistake as we did (the LoadModuleOperation is a good example) and will have bugs to track down.
                  I think the misuse comes from the fact that in the Flex platform, developers attach listeners to a component and expect them to be triggered whatever the sync/async behaviour behind.
                  With the operations API, it is not the case anymore. you have to know how the implementation works to use the appropriate interface (IOperation/IAsyncCommand).

                  Now, that's right, I should have read the documentation before starting coding my operation
                  RTFM !

                  Comment


                  • #10


                    Hey,

                    I think I'll just add some clarifications to the ASDoc as well, that way it'll hopefully stick out more as well when using the classes/interfaces in someone's code.
                    The case of the LoadModuleOperation is indeed a nasty one, I didn't know that the ModuleManager checks for itself if the given URL has already been loaded, and if so, immediately dispatches the event. I will add a timer to that class that executes the load() a bit later, this is a trick I've seen being used in the Flex fraemwork as well, to be able to add eventlisteners. I think it was somewhere in the ResourceManager, I'll try and look it up and post back here.

                    cheers,

                    Roland

                    Comment


                    • #11
                      found it

                      Yea, found it, check out this class (I'm using the Flex 4 SDK):

                      mx.resources.ResourceManagerImpl

                      go to line 482 and you'll see the trick they're pulling, I'll implement this in the same way in the LoadModuleOperation, that should negate the need for your IAsyncCommand implementation.

                      So, this does indeed turn out to be a bug of sorts, so this thread has definitely been useful!

                      cheers,

                      Roland

                      Comment


                      • #12
                        implemented

                        Hey guys,

                        I did a quick implementation of the timer trick in the operations that load modules, I don't have time to test it thoroughly though since I have to get on a train ina few hours and also need to work on my project in the mean time.
                        If any of you have the chance to test the LoadModuleOperation, to see if the event handlers get called after loading the same module for a second time, that would be great.

                        thanks a lot in advance,

                        Roland

                        Comment


                        • #13
                          it works

                          After a quick test, it seems to be working.
                          Now just for my culture, concerning the implementation : wouldn't have it been simpler to use the setTimeout function rather than a Timer ? :

                          So this :
                          Code:
                          			setTimeout(function():void {
                          				moduleInfo.load(applicationDomain, securityDomain);
                          			}, 0);
                          Instead of :
                          Code:
                          			var timer:Timer = new Timer(0);
                          			var timerHandler:Function = function(event:TimerEvent):void {
                          					timer.removeEventListener(TimerEvent.TIMER, timerHandler);
                          					timer.stop();
                          					timer = null;
                          					moduleInfo.load(applicationDomain, securityDomain);
                          				}
                          			timer.addEventListener(TimerEvent.TIMER, timerHandler, false, 0, true);
                          			timer.start();
                          Any difference between the two forms ?

                          Thanks for your time !

                          Comment


                          • #14
                            Hi,

                            AFAIK the 2 are the same. The docs for setTimeout() state the following though:

                            Instead of using this method, consider creating a Timer object, with the specified interval, using 1 as the repeatCount parameter (which sets the timer to run only once).
                            Unfortunately, I don't know why that is. Anyone?

                            regards,
                            Christophe

                            Comment

                            Working...
                            X