7 Replies Latest reply on Sep 17, 2010 9:22 AM by Devtron

    Synchronously call HTTPService?

    Gregory_Pierce
      I need to call several HTTPServices in succession - one to look up a user token, another to request an array of data, and a third to pull down this data. Looking at HTTPService I need to add listeners for each one of these calls and have some sort of function process the result from each call which looks horribly cumbersome as there is no interaction there. All I want to do is put up a loading icon, call the services, get the result parsed, and turn off the loading icon.

      Is there no way to do this with Flex and Actionscript3?
        • 1. Re: Synchronously call HTTPService?
          JKohn99 Level 1
          Unfortunately no there is no way around the fact that Flex and the Flash Player are
          asynchronous. This is because of the way the browsers work among other things.

          To achieve a this result you will need to have the result handler for function 1
          make the next call and then the handler for function 2 can call function 3.

          • 2. Re: Synchronously call HTTPService?
            Gregory_Pierce Level 1
            Thanks but ugh, that's awful because that first step (getting the users token) is reused by a bunch of methods. Is there a way to pass a handle to an ActionScript function so I can pass function one the function that it should call next? That way at least I wouldn't have to repeat the get user token a bagillion times throughout my code.

            That is such a huge oversight that could be so easily remedied by having a default event listener that waited for the response before returning control to the calling method such that when control was returned the HTTPService request would have concluded. Man, so much spaghetti will be created from this.
            • 3. Re: Synchronously call HTTPService?
              ProfessorC Level 1
              I guess I'm not really seeing the issue you're trying to solve.
              If you need to parse the results, you need to parse the results. That process is going to involve the same amount of complexity whether it's done within a callback function (attached to a listener) or somewhere else.

              The only way to guarantee that several HTTPServices are executed in succession, the second waiting until the first has finished*, is to call each successive service from the callback of the one that preceded it.

              But, in truth, it really isn't all that cumbersome. Using the mx:HTTPService tag makes it a little easier to set up the callbacks:

              <mx:Application ... creationComplete="appInit()">
              <mx:Script>
              <![CDATA[
              import flash.events.*;
              import flash.net.URLLoader;
              import flash.net.URLRequest;
              import mx.rpc.events.ResultEvent;

              public function appInit():void{
              svcUserData.send();
              }

              public function userTokenOK(event:ResultEvent):void{
              // call to svcUserData has completed, process and call next service
              var userTokenResponse=event.result;
              :
              :
              svcRequestDataArray.send();

              }
              public function dataArrayOK(event:ResultEvent):void{
              // call to svcRequestDataArray has completed, process and call next service
              var requestDataArrayResponse=event.result;
              :
              :
              svcPulldownData.send();

              }

              public function pulldownDataOK(event:ResultEvent):void{
              // call to svcPulldownData has completed, process data
              var pulldownDataResponse=event.result;

              }

              public function httpServiceFailed(event:Event):void{
              // failure in one of the services, do what you need to do
              mx.controls.Alert.show("Problem loading...");
              :
              }
              ]]>
              </mx:Script>


              <mx:HTTPService result="userTokenOK(event)" fault="httpServiceFailed(event)" id="svcUserTokenLookup" url=" http://www.domain.com/..." method="POST" />
              <mx:HTTPService result="dataArrayOK(event)" fault="httpServiceFailed(event)" id="svcRequestDataArray" url=" http://www.domain.com/..." method="POST" />
              <mx:HTTPService result="pulldownDataOK(event)" fault="httpServiceFailed(event)" id="svcPulldownData" url=" http://www.domain.com/..." method="POST" />


              Or you can create HTTPService loaders on the fly

              var loader:URLLoader = new URLLoader();
              var req:URLRequest = new URLRequest(' http://www.domain.com/usertokenservice/...');
              var params:URLVariables = new URLVariables();
              params.tcommand="whatever params you need";
              req.method=URLRequestMethod.POST;
              req.data = params;
              loader.addEventListener(Event.COMPLETE,userTokenOK);
              loader.load(req);

              and then the userTokenOK would be pretty much the same as above, except it would create another loader for the dataArray, and so on.

              But... I do wonder why you have one service to request data, and another to pull it down. Why not just return the data in the request. Or, better yet, have one service that, when passed a user token, looks up the array of data and returns it to Flex. This way you're entire issue is solved. You're calling just one service, and don't need to worry about Flex's threading at all.



              * I qualify this statement because, with the proliferation of AJAX, "synchronous" and "asynchronous" have come to mean different things to different people. Just look at the exactly opposite definitions of "asynchronous" on Dictionary.com vs. Wikipedia.com

              Dictionary.com
              http://dictionary.reference.com/browse/asynchronous

              a·syn·chro·nous /eɪˈsɪŋkrənəs/
              –adjective 1. not occurring at the same time.
              2. (of a computer or other electrical machine) having each operation started only after the preceding operation is completed.



              Wikipedia.com
              http://en.wikipedia.org/wiki/Asynchronous_I/O

              Asynchronous I/O, or non-blocking I/O, is a form of input/output processing that permits other processing to continue before the transmission has finished.



              • 4. Synchronously call HTTPService?
                ProfessorC Level 1
                quote:

                Originally posted by: Gregory Pierce
                Thanks but ugh, that's awful because that first step (getting the users token) is reused by a bunch of methods. Is there a way to pass a handle to an ActionScript function so I can pass function one the function that it should call next? That way at least I wouldn't have to repeat the get user token a bagillion times throughout my code.

                That is such a huge oversight that could be so easily remedied by having a default event listener that waited for the response before returning control to the calling method such that when control was returned the HTTPService request would have concluded. Man, so much spaghetti will be created from this.


                No, it's actually not a huge oversight. And, in actuality, it should reduce spaghetti code.

                But... to address the issue of "calling get user token a bagillion times"... why not call it once and store the result in a variable?

                Then all the callbacks that follow will be able to use the value of that variable, rather than call get user token? No need to go back to the trough if you already have the data you need.

                You can pass additional parameters to the callbacks. In some instances, it means just adding a parameter object as a third parameter to the addListener function, but it generally means a slightly more complicated method of setting up your own class to hold additional parameters. Whichever way you choose, you can set up a parameter of type function, and in the callback simply do this.callLater(theFunctionIwantToRun);
                • 5. Re: Synchronously call HTTPService?
                  Gregory_Pierce Level 1
                  Because in many cases (like the Flickr API) you are regenerating something based on what's passed in.

                  So lets take the FlickrAPI, you need to generate a signature for the functions you are calling for any signed method. Then after you do that, you call the signed method (a simpler example than what I've got). In this case you have some code that looks like:

                  public function generateSignatureForMethod( methodName:String ):String
                  {
                  var signature:String;

                  signature = MD5.hash( secret + "api_key" + apiKey + "method" + methodName );

                  trace( "Signature = " + signature );

                  return signature;
                  }

                  So you have to call this before any other signed methods. So lets take a look at getting pictures:

                  So lets take a look at a use case from a simple example:

                  public function authenticate():void
                  {

                  var ws:HTTPService = new HTTPService();
                  ws.url = restURL; /
                  ws.addEventListener('fault', faultHandler);
                  ws.addEventListener('result', processToken );
                  ws.request = {method: 'myRESTAPI.getMeTheToken', api_key: apiKey, api_sig: generateSignatureForMethod("myRESTAPI.getMeTheToken") }; // Set up args to send
                  ws.send(); // Send request to flickr
                  }

                  Then I need to process the token, so I have another function. 2 Functions as opposed to having:

                  public function getUserToken( userName:String, passwordMD5:String )
                  {
                  try
                  {
                  token = authenticate( userName, passwordMD5 );
                  Account account = getUserAccount( token );
                  }
                  catch( Exception e )
                  {
                  trace("Opps something blew up " + e.getMessage() );
                  }
                  }


                  That is FAR more readable than tracing through a string of function calls. If the HTTPService call fails, fine throw an exception - I'll deal with it. But I don't need to call multiple methods to accomplish the effect. I don't need my HTTP calls to be asynchronous if I need the result of them before I can do anything useful. Asychronous behavior should be an option, not the only behavior. That is absolutely an oversight based on some very specific use cases.
                  • 6. Synchronously call HTTPService?
                    Ansury Level 3
                    quote:

                    Originally posted by: Gregory Pierce
                    I don't need my HTTP calls to be asynchronous if I need the result of them before I can do anything useful. Asychronous behavior should be an option, not the only behavior. That is absolutely an oversight based on some very specific use cases.


                    That's not really true. Well, technically it almost is--YOU can't do anything useful. But unfortunately the browser needs to remain responsive to the user. Blocking-behavior would prevent the browser from reacting to the user AT ALL until a call returns. Very very bad.. it'd be like this:

                    <?xml version="1.0" encoding="utf-8"?>
                    <mx:Application xmlns:mx=" http://www.adobe.com/2006/mxml" layout="absolute">

                    <mx:Script>
                    <![CDATA[
                    private function test():void {

                    var i:int = 0;
                    while (i < 5000000) {
                    i++;
                    if (i % 100 == 0)
                    trace(i);
                    }

                    trace("done");
                    }
                    ]]>
                    </mx:Script>
                    <mx:Button x="161" y="135" label="Test" click="test();"/>
                    </mx:Application>


                    You may need to adjust the counter depending on your system's speed, but try it and you should see what I mean.

                    There's nothing stopping you from doing something like this (hiccupping the browser), setting up a loop that waits until a variable changes from null to some value, but I don't think you'd want to do that... Not sure if Flash has a way around this issue. I guess if you could kick off your own threads, it would be possible to do what you want, but that's a different discussion I guess.

                    There is the feature suggestion link (below, sig), I guess, if you want to ask for creating threads. But I'm not sure if it'll ever happen.
                    • 7. Re: Synchronously call HTTPService?
                      Devtron Level 3

                      ^ Ansury, how is that even considered programming? That's what I call "scripting". Good luck with that.