11 Replies Latest reply on Feb 3, 2011 12:33 PM by JoshBeall

    Using AsyncToken with my own events

    JoshBeall Level 1

      Hi All,

       

      One of the things I really like about the service proxy classes that Flash Builder creates for you is that they return an AsyncToken for each service method that I call. That allows me to attach an IResponder (I use either mx.rpc.Responder or mx.rpc.AsyncResponder) with a particular set of code to that particular method call.  If I were to use addEventListener to attach a listener for the result event, then all my service calls would hit that same listener function.

       

      So, I've got an class that dispatches events.  When you call an async method on my object, then you have to addEventListener on the object and listen for the result event.  But I would love it if I could use the same paradigm that I described above, where my async methods return an AsyncToken.  Then the caller can take that token and attach whatever listeners it wants, and know that those listeners will only get fired when that particular service call comes back with the result (or failure).

       

      Can anyone give me some insight on how I might do this?  I haven't been able to figure this out yet.  I see that if I want to instantiate a new AsyncToken, the constructor wants something that implements the IMessage interface, but I'm still working on deciphering how that works...

       

        -Josh

        • 1. Re: Using AsyncToken with my own events
          JoshBeall Level 1

          When I go to the IMessage interface definition in Chrome, I see nothing.  In IE8, I see a bunch of information.

          http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/messaging/messages/I Message.html

           

          Anybody else getting this Chrome vs IE behavior?

          • 2. Re: Using AsyncToken with my own events
            JoshBeall Level 1

            Here's what I know I must need to do:

             

            1) Create an AsyncToken and pass the constructor an IMessage

            2) The IMessage implementation must notify the AsyncToken when the async operation completes, along with the result object

            3) The AsyncToken then notifies all IResponders in the AsyncToken.responders collection that the event has completed, and calls IResponder.result (or IResponder.fault), passing in the result object

             

            Where I'm stuck is at step 2.  I'm reading over the definition of the IMessage interface and I can't figure out what is going to notify the AsyncToken that the async operation has completed.  Here are all the members that you implement to have a valid IMessage implementation:

             

            Public Properties
            Property Defined By
            body : Object
            The body of a message contains the specific  data that needs to be delivered to the remote destination.
            IMessage
            clientId : String
            The clientId indicates which client sent the  message.
            IMessage
            destination : String
            The message destination.
            IMessage
            headers : Object
            Provides access to the headers of the  message.
            IMessage
            messageId : String
            The unique id for the message.
            IMessage
            timestamp : Number
            Provides access to the time stamp for the  message.
            IMessage
            timeToLive : Number
            The time to live value of a message indicates  how long the message should be considered valid and deliverable.
            IMessage
            Public Methods
            Method Defined By
            This method will return a string  representation of the message.
            IMessage
            Which of those is going to be used to notify the AsyncToken that the operation is complete?
            Thanks!
              -Josh

            • 3. Re: Using AsyncToken with my own events
              JoshBeall Level 1

              I found this post here:

              http://stackoverflow.com/questions/2787778/how-to-fake-a-asynctoken-return-in-actionscript -3

               

              Which seems to suggest that you have to use the mx_internal namespace--which I'm hesitant to do.  Is there no other way to return AsyncToken objects from my own methods?  I'd rather avoid the mx_internal namespace--if it was "safe" to use those methods, they wouldn't be in mx_internal, would they?

               

                -Josh

              • 4. Re: Using AsyncToken with my own events
                JoshBeall Level 1

                Still haven't been able to figure out the right way to do this... is there a way to do this?

                • 5. Re: Using AsyncToken with my own events
                  JoshBeall Level 1

                  I'm guessing from the lack of response the only way to do this is with mx_internal, like this:

                   

                  public function list():AsyncToken
                  
                       var rssFeed:Array = [rss,rss,rss];
                       var token:AsyncToken = createToken(rssFeed);
                       token.addResponder(new AsyncResponder(resultHandler, null));
                       return token;
                  
                    }
                  
                    private function resultHandler(event:ResultEvent, token:AsyncToken = null):void
                    {
                       event.token.dispatchEvent(event);   
                    }
                  
                  
                    protected function createToken(result:Object):AsyncToken
                    {
                       var token:AsyncToken = new AsyncToken(null);
                       setTimeout(applyResult, Math.random()*500, token, result);
                       return token;
                    }
                  
                    private function applyResult(token:AsyncToken, result:Object):void
                    {
                       mx_internal:token.setResult(result);
                       var event:ResultEvent = new ResultEvent(ResultEvent.RESULT, false, true, result, token);
                       mx_internal:token.applyResult(event);
                       trace(token);
                    }
                  
                  

                   

                   

                  Source: http://stackoverflow.com/questions/2787778/how-to-fake-a-asynctoken-return-in-actionscript -3

                   

                  In this case, he just wanted to mock an AsyncResult for testing purposes.

                   

                  I think I can see how I would apply it to my situation, where I would return an AsyncToken from my asynchronous method on my object, and then inside my object I would setup an event listener that would listen for the result event.  That result event would then "apply" the result to the AsyncToken (which my object retained a copy of when it called the service).

                   

                  At least, I think that would work... I haven't actually tested yet.  So I might run into some issues if I were try and implement this.

                   

                  As I said, I'm wary of using the mx_internal namespace.  Are my fears unfounded?  Should I not worry about doing this?

                  • 6. Re: Using AsyncToken with my own events
                    JabbyPandaUA Level 3

                    Yes, I am with you.

                     

                    This page in Livedocs contains Javascript error that prevents Chrome browser from correct renderering it.

                     

                    IE browser displays and Javascript error, but at least displays the contents of the page.

                     

                    Worth opening a FlexDocs related bug in JIRA.

                    • 7. Re: Using AsyncToken with my own events
                      JabbyPandaUA Level 3

                      Have a look at listing of TestUtil "delayResultHelper" method from Swiz framework, I guess it will be helpful to you

                       

                      http://code.google.com/p/swizframework/source/browse/trunk/src/main/flex/org/swizframework /util/TestUtil.as

                       

                      Full discussion on topic:

                       

                      "ActionScript 3 AsyncToken implementation"

                      http://stackoverflow.com/questions/1052858/actionscript-3-asynctoken-implementation

                       

                       

                      public static function mockFaultEvent( event:FaultEvent, delay:Number = 500, showBusyCursor:Boolean = true ):AsyncToken
                                      {
                                              if ( showBusyCursor )
                                              {
                                                      CursorManager.setBusyCursor();
                                              }
                      
                                              // Create a new token that we can return so that responders can be attached to it
                                              var token:AsyncToken = new AsyncToken( null );
                      
                                              // Wait until the delay expires, then notify the token responders
                                              // of the result to simulate asynchronous behavior.
                                              setTimeout( delayResultHelper, delay, token, event, showBusyCursor );
                      
                                              return token;
                                      }
                      
                                      /**
                                       * Helper method that we can call after a delay to notify token responders
                                       * of either a fault or result.
                                       * 
                                       * @param token The <code>AsyncToken</code> instance to call the responder on.
                                       * @param event Either a <code>ResultEvent</code> or a <code>FaultEvent</code>.  The
                                       *              type of the event determines which responder to call on the token.
                                       * @param showBusyCursor When <code>true</code>, the busy cursor is removed when
                                       *              this method executes.
                                       */
                                      internal static function delayResultHelper( token:AsyncToken, event:Event, showBusyCursor:Boolean ):void
                                      {
                                              if ( showBusyCursor )
                                              {
                                                      CursorManager.removeBusyCursor();
                                              }
                      
                                              // Bail out if the token is not a valid reference
                                              if ( token == null )
                                              {
                                                      return;
                                              }
                      
                                              var responder:IResponder;
                      
                                              // Determine the type of the event and call the appropriate responder
                                              if ( event is ResultEvent )
                                              {
                                                      for each ( responder in token.responders )
                                                      {
                                                              responder.result( event );
                                                      }
                                              }
                                              else if ( event is FaultEvent )
                                              {
                                                      for each ( responder in token.responders )
                                                      {
                                                              responder.fault( event );
                                                      }
                                              }
                                      }
                      

                      • 8. Re: Using AsyncToken with my own events
                        JoshBeall Level 1

                        Thanks for that StackOverflow link--that helped me understand how to do this.  I got a little "proof of concept" code working, before running into Flex bug FB-26409.  Now I've got a get a workaround for that in place, or figure out what the fixed SDK version is and use that.

                         

                          -Josh

                        • 9. Re: Using AsyncToken with my own events
                          JoshBeall Level 1

                          Ok, here's what I've done.  I'm interested in feedback if this is the right way to do this.

                           

                          A particular question I have is, should I pass null to the AsyncContructor, or pass in the IMessage associated with the HTTP call I am making?

                           

                          public function saveAppReturnAsyncResponder(app:MembershipApplication):AsyncToken{
                              var encoder:JSONEncoder = new JSONEncoder();
                              var json:String = encoder.encode(app);
                              
                              // This is the token attached to the actual asynchronous HTTP call.
                              var serviceToken:AsyncToken = this.service.SaveMembershipApplication(json);
                              
                              // This is the token that we will return to the caller. Should I just pass null
                              // to this constructor? It seems to work just fine if I pass in null, but I don't
                              // know if I'm setting myself up for null reference exceptions being thrown unexpectedly.
                              var resultToken:AsyncToken = new AsyncToken(serviceToken.message);
                              
                              // Now create a responder that will handle the HTTP call results.
                              var serviceResponder:AsyncResponder = new AsyncResponder(
                              
                                  // Result function
                                  function(result:Object, token:Object = null):void{
                                      // Here, you would do whatever additional processing of the results you want to do.
                                      // For this example, all we'll do is encapsulate the result in a custom event.
                                      // This custom event will be passed to any IResponders attached to the resultToken.
                                      var event:ApplicationSavedEvent = new ApplicationSavedEvent(result, token);
                                      
                                      // Loop over ever IResponder in the resultToken.responders array,
                                      // and call the result method on each IResponder instance
                                      for each(var responder:IResponder in resultToken.responders){
                                          responder.result(event);
                                      }
                                  },
                                  
                                  
                                  // Fault function
                                  function(error:Object, token:Object = null):void{
                                      // Same as in the result function, we'll loop over each IResponder
                                      // in the resultToken.responders collection and call the fault
                                      // function.
                                      for each(var responder:IResponder in resultToken.responders){
                                          responder.fault(error);
                                      }
                                      
                                  },
                                  
                                  // Token that is passed to result or fault function.
                                  app)
                                  
                              serviceToken.addResponder(serviceResponder);
                              
                              // By returning the resultToken, we give the calling code an opportunity to attach IResponders
                              // to the resultToken, and those IResponders can then handle the result of faults of the service.
                              return resultToken;
                          }
                          

                          • 10. Re: Using AsyncToken with my own events
                            JabbyPandaUA Level 3

                            Why do you want to return "resultToken", but not "serviceToken" by calling this method?

                            • 11. Re: Using AsyncToken with my own events
                              JoshBeall Level 1

                              In that example the only reason was to alter the event type that is passed to the resultToken's result function.  I stripped out a lot of my processing code to make the example simpler.

                               

                              But, in my actual application, there is a great deal additional logic that happens inside serviceToken's result function.  It translates the raw result that comes back from the HTTP service into an object that is more usable (and expected) by the caller.

                               

                              This allows me to have a function like "saveApplication" that handles all the details of sending the result off to the HTTP service, and processing the result that comes back from the HTTP service, and hiding those details from the caller.  The caller just needs to call the method and then put a listener on the AsyncToken for when the result comes back, and the result that the caller gets will be packaged up and ready to use--no need to worry about the details of processing the result object that came back from the HTTP service.

                               

                                -Josh