11 Replies Latest reply on Jan 14, 2010 2:45 AM by pauland

    Using Nested Functions

    MrFork Level 1

      Hello Im trying to create a dynamic HTTP function that will allow me to dynamically return any result into an array. In the below code within the nested function, Im finding that inside the nested function I can gain access to the data, however outside of the nested function, the data is not being returned. Could someone please help me out? Im pulling my hair out here. See code below:

       

      <?xml version="1.0" encoding="utf-8"?>
      <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
         
          <mx:Script>
              <![CDATA[
                  import mx.rpc.http.mxml.HTTPService;
                  import mx.collections.ArrayCollection;
                  import mx.rpc.events.ResultEvent;
                 
                  public function sendQuery(db:String,query:String):ArrayCollection
                  {
                      var returnData:ArrayCollection = new ArrayCollection;
                      var tmpArray:ArrayCollection = new ArrayCollection;
                     
                      var obj:Object = new Object();
                      obj.database = db;
                      obj.query = query;
                     
                      var httpService:HTTPService = new HTTPService;
                      httpService.method = "POST";
                      httpService.url = "http://localhost/return_SQL.php";
                      httpService.useProxy = false;
                      httpService.addEventListener(ResultEvent.RESULT, recieveData);
                      httpService.send(obj);
                     
                      function recieveData(evt:ResultEvent):ArrayCollection
                      {
                          tmpArray = evt.result.data.graphdata;
                          return tmpArray;
                          // test.dataprovider = tmpArray !!!WORKS!!!
                      }
                      // test.dataProvider = tmpArray; !!! DOESNT WORK !!!
                     
                      return returnData;
                  }
                 
                  public function testExer():void
                  {
                      var myData:ArrayCollection = new ArrayCollection();
                      myData = sendQuery('visa','SELECT * from redemption_data');
                  }
                 
              ]]>
          </mx:Script>
         
          <mx:DataGrid x="214" y="112" id="test">
         
          </mx:DataGrid>
          <mx:Button x="233" y="262" label="Button" click="testExer()"/>
         
      </mx:Application>

        • 1. Re: Using Nested Functions
          Gregory Lafrance Level 6

          Don't use function in a function for result handlers.

           

          If this post answers your question or helps, please mark it as such.


          Greg Lafrance - Flex 2 and 3 ACE certified

          www.ChikaraDev.com

          Flex / AIR Development, Training, and Support Services

          1 person found this helpful
          • 2. Re: Using Nested Functions
            MrFork Level 1

            Hello Greg, many thanks for your speedy response.

             

            The reason I have included a function within a function is because I want to create a function that allows me to pass an SQL statement into it, and then return an array collection.

             

            Therefore I could write something like this -

             

            var myData:Arraycollection = new Arraycollection ();

            myData = sendQuery("Select * from sales");

             

            With this in mind do you have ideas as to how I could create this within a flex function? Having the result handler outside of the sendQuery wouldn'y give me the desired result.

             

            p.s. our company regularly develops using Adobe Flash and could be interested in your services.

             

            Thanks

             

            Craig

            • 3. Re: Using Nested Functions
              msakrejda Level 4

              I don't understand what you are trying to do. You *can* define nested functions like that, but I don't see why you would expect the dataProvider assignment to work outside the event listener. Your nested function is an event handler--it gets called when the event is dispatched. At that time, you reassign tmpArray to the result of the call. If you don't update test.dataProvider with the new tmpArray, it will still be referring to the old ArrayCollection defined at the top of the function (even though tmpArray is now referring to something else).

              1 person found this helpful
              • 4. Re: Using Nested Functions
                msakrejda Level 4

                You can't do that. Http service calls are asynchronous in Flex, meaning that the result of the service will not be available by the time the function completes (since the function itself executes synchronously). You need to use an asynchronous method (event handlers, callbacks, or even binding).

                • 5. Re: Using Nested Functions
                  MrFork Level 1

                  Ok well this is where my Flex knowledge lacks then...

                   

                  I will have to do it another way - many thanks for all your help guys.

                   

                  Craig

                  • 6. Re: Using Nested Functions
                    msakrejda Level 4

                    Ah, I see. You *might* be able to get somewhere with an approach like the following:

                     

                                public function sendQuery(db:String,query:String):ArrayCollection
                                {
                                    var returnData:ArrayCollection = new ArrayCollection;
                                   
                                    var obj:Object = new Object();
                                    obj.database = db;
                                    obj.query = query;
                                   
                                    var httpService:HTTPService = new HTTPService;
                                    httpService.method = "POST";
                                    httpService.url = "http://localhost/return_SQL.php";
                                    httpService.useProxy = false;
                                    httpService.addEventListener(ResultEvent.RESULT, recieveData);
                                    httpService.send(obj);
                                   
                                    function recieveData(evt:ResultEvent):ArrayCollection
                                    {
                                        for each (var o:Object in evt.result.data.graphdata) {

                                            returnData.addItem(o);

                                        }
                                    }
                                   
                                    return returnData;
                                }

                     

                     

                     

                    This should retrun an ArrayCollection that will be (magically) populated when the service call completes (disclaimer: I haven't tried this). However, I'm not sure if it's safe to create and call an HTTPService inside a function without saving a reference to it--I believe the HTTPService might be elligible for garbage collection right away, which could screw things up if GC kicks in. If you store the HTTPService somewhere (e.g., in a member variable), this might actually work.

                    • 7. Re: Using Nested Functions
                      levancho Level 3

                      msakrejda wrote:

                      I'm not sure if it's safe to create and call an HTTPService inside a function without saving a reference to it--I believe the HTTPService might be elligible for garbage collection right away, which could screw things up if GC kicks in. If you store the HTTPService somewhere (e.g., in a member variable), this might actually work.

                      not reallly,

                       

                      httpService.addEventListener(ResultEvent.RESULT, recieveData);

                      it still has event listeners attached to it, so its not going to be garbage collected like that.

                       

                      also I dont understand why  nested functions should not be used? that is a little bold statement without explaination, actually AS3 is a big hierarchy of nested functions, (somewhat) and there is absolutely nothing wrong with having function inside function, as long as you can work with scope and adjusted whenever you need, thats where apply, and call comes handy and real power of AS3.

                      • 8. Re: Using Nested Functions
                        MrFork Level 1

                        i tried this and it seems to be working ok...the only difference is the nested function should return nothing.

                         

                        Im running some testing now to see how I get on with it.

                         

                        Thanks

                        • 9. Re: Using Nested Functions
                          msakrejda Level 4

                          >it still has event listeners attached to it, so its not going to be garbage collected like that.

                           

                          It has listeners attached to it, but nothing has a reference to the service. Garbage collection works in terms of a tree of references from the root (which I guess would be the Stage in Flex). If the HTTPService has a reference to the listener, the *listener* will not be GCed until the service is, but since nothing has a reference to the service once the function returns, I'm pretty sure the service *will* be elligible for GC.

                          • 10. Re: Using Nested Functions
                            MrFork Level 1

                            Hello Guys,

                             

                            I've had a go with the code in which we worked through yesterday however, when coming to use the code I'm finding issues with the arrays in which im assigning, using the function. Please see below.

                             

                            You'll notice that the dataproviders contain the data in which was requested, however, the new array collections (myData1, myData2, myData3, myData4) do not contain the returned data - their lenghts are all 0.

                             

                            Please excuse my lack of knowledge in the workings of flex, im fairly new to this. Any help would be much appreciated.

                             

                             

                            <?xml version="1.0" encoding="utf-8"?>
                            <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
                               
                                <mx:Script>
                                    <![CDATA[
                                        import mx.rpc.http.mxml.HTTPService;
                                        import mx.collections.ArrayCollection;
                                        import mx.rpc.events.ResultEvent;
                                       
                                        public function sendQuery(db:String,query:String):ArrayCollection
                                        {
                                            var returnData:ArrayCollection = new ArrayCollection;
                                           
                                            var obj:Object = new Object();
                                            obj.database = db;
                                            obj.query = query;
                                           
                                            var httpService:HTTPService = new HTTPService;
                                            httpService.method = "POST";
                                            httpService.url = "http://localhost/visa/php/return_SQL.php";
                                            httpService.useProxy = false;
                                            httpService.addEventListener(ResultEvent.RESULT, recieveData);
                                            httpService.send(obj);
                                           
                                            function recieveData(evt:ResultEvent):ArrayCollection
                                            {
                                                for each (var o:Object in evt.result.data.graphdata) {
                                                   
                                                    returnData.addItem({date:o.date});
                                                }
                                                return returnData;
                                            }
                                            return returnData;   
                                        }
                                       
                                        public function testExer():void
                                        {
                                            var myData1:ArrayCollection = new ArrayCollection();
                                            var myData2:ArrayCollection = new ArrayCollection();
                                            var myData3:ArrayCollection = new ArrayCollection();
                                            var myData4:ArrayCollection = new ArrayCollection();
                                           
                                            myData1 = sendQuery('visa','SELECT * from redemption_data');
                                            myData2 = sendQuery('visa_historic','SELECT * FROM entrancestotal');
                                            myData3 = sendQuery('visa_historic','SELECT * FROM googlesummarytotal');
                                            myData4 = sendQuery('visa_historic','SELECT * FROM googletotal');
                                           
                                            trace (myData1.length); // length = 0
                                            trace (myData2.length); // length = 0
                                            trace (myData3.length); // length = 0
                                            trace (myData4.length); // length = 0
                                           
                                            test1.dataProvider = myData1; // Data provider is updated
                                            test2.dataProvider = myData2; // Data provider is updated
                                            test3.dataProvider = myData3; // Data provider is updated
                                            test4.dataProvider = myData4; // Data provider is updated
                                        }
                                       
                                    ]]>
                                </mx:Script>
                               
                                <mx:DataGrid x="10" y="10" id="test1"/>
                                <mx:DataGrid x="10" y="292" id="test2"/>
                                <mx:DataGrid x="702" y="292" id="test3"/>
                                <mx:DataGrid x="702" y="22" id="test4"/>
                               
                                <mx:Button x="233" y="262" label="Button" click="testExer()"/>
                               
                            </mx:Application>

                            • 11. Re: Using Nested Functions
                              pauland Level 4

                              The retrieval of the data from the database is asynchronous and not instant. Your trace statements for the length of the collections show 0 because the data hasn't yet been loaded.