Flash Platform
All AS3 programmers have had to cope with situations where what they can see on the screen is different to what their code is telling them. Within Air, Flex and Flash applications the two main contributors to this are; the Flash Player is single threaded, apart from a few exceptions in Air, and internally is based around a frame rate.

Have you ever tried to measure something you have just added to the Display List and even though you can see it on the screen your measurement, something like width or height, is coming back as 0? How about when you are preloading, does your preloader get stuck at 10% and then jump to 100% at the end?

Elastic Racetrack Cure

First things first, download my Actionscript 3 utilities.

The reason why sometimes you can't measure items you have just added to the Display List in AS3 is because of the "Elastic Racetrack". If you don't know what that is then I recommend you scoot off and read up about it first. Understanding this is essential for intermediate / advanced Actionscripters.

One cure for the Elastic Racetrack is to use Grant Skinner's excellent Chunker approach. It's not always suitable for all situations though and a more straight forward approach is to simply wait 1 frame before carrying on with your measurement. I recently had a project where I needed to wait 1 frame in quite a few places and so the FrameBuffer was born.

Create a new Flex 4 project and type in the following code :

<mx:Application
       
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:mx="library://ns.adobe.com/flex/mx"
        xmlns:s="library://ns.adobe.com/flex/spark"

        creationComplete="setUp()"
         
        >
       
        <fx:Script>
       
                <![CDATA[
                       
                        import mx.collections.ArrayList;

                        [Bindable]
                        private var _years:ArrayList = new ArrayList();
                               
                        private function setUp() : void{
                               
                                        _years.source = ["2010" , "2009" , "2008" ];
                                       
                                        output.text =   "       yearTabBar.width = " + yearTabBar.width;                       
                        }
                               
                ]]>
                       
        </fx:Script>
       
        <s:TabBar
                                                       
                id="yearTabBar"
                dataProvider="{_years}"

        />
                       
        <mx:Text id="output"></mx:Text>  
       
</mx:Application>

When you publish you will see the Flex 4 Spark TabBar.


Spark Component TabBar


Frustratingly the output text will tell you that :

yearTabBar.width = 0

The reason is of course that we need to wait 1 frame for the Flash Player to get through the render part of it's cycle. During this part the component is be measured and its width recorded.

There are many situations where this crops up and many ways of dealing with it. The best approach is to architect your application differently so it does not fall victim to this. Your Views should go through separate Draw and Layout phases and this problem will be avoided. Like using Chunker, which I linked to above, this is not always possible or desirable. It's not going to help your preloader for example.

The easiest way is to set up an Event Handler to listen for Event.ENTER_FRAME and then to retrieve the width. This is of course quite easy to do but on a recent project it was cropping up a bit and I was looking for an straight forward way achieve this and so I create the FrameBuffer utility.

If you haven't done so already make the FrameBuffer code available to your project.

Step 1. Create a folder called com inside your src folder.
Step 2. Create a garethshapiro folder inside your com folder.
Step 3. Add the utilities folder from the download and everything in it to the garethshapiro folder.

Change the code you typed above to include the lines that have //2 at the end below

<fx:Script>
       
                <![CDATA[
                       
                        import mx.collections.ArrayList;
                        import com.garethshapiro.utilities.framebuffer.FrameBuffer;                 // 2
                               
                        [Bindable]
                        private var _years:ArrayList = new ArrayList();
                               
                        // Create the TabBar
                        private function setUp() : void{
                               
                                _years.source = ["2010" , "2009" , "2008" ];
                                       
                                output.text =   "       yearTabBar.width = " + yearTabBar.width;
       
                                var __frameBuffer : FrameBuffer = new FrameBuffer();                // 2
                                       
                                __frameBuffer.buffer(   measureTabBar   );                                 // 2                                
       
                        }                                                                                                       // 2
                               
                               
                        private function measureTabBar() : void {                                           // 2
                               
                                output.text =   "       yearTabBar.width = " + yearTabBar.width;      // 2
                                       
                        }                                                                                                        // 2

                ]]>
                       
        </fx:Script>

This time the output text will tell you :

yearTabBar.width = 211

A result. Happy face.

By default the FrameBuffer will wait 1 frame. If you want it to wait longer then you can supply a second argument a Number which is the number of frames to wait.

__frameBuffer.buffer(   measureTabBar   , 5 );


It is also very likely that you would need to supply arguments to the method that is to be called and this is possible through another optional parameter. The arguments to be supplied are wrapped up in an Array.

__frameBuffer.buffer(   measureTabBar   , 5 , ["Trace Message" , true ]);

Preloading

To get this to help you in preloading simply divide your preloading tasks into pieces and at the end of each piece use the FrameBuffer to wait one frame before moving on to the next piece. If you have a large amount of data then instead of having one for loop handle it all use some variables to keep track of how far you are in the data and only process a small amount each time, wait 1 frame, and carry on.

Context

This code is still dependant on the frame rate of the Flash Player. If you give the player too much to do in one frame everything in your application, including the FrameBuffer is going to wait until that code is finished.

The FrameBuffer is the easiest way but not the most efficient way of solving this problem. If you are writing code that is sensitive to performance then there are better ways of achieving results, two of which I have mentioned in this article. We are adding a type of pause into our program. Expecting better performance this way is the same as expecting more flavour from food by adding water.

That aside quite often user feedback is more important than performance, especially when the difference is milliseconds. This is not an article aimed at C++ programmers writing financial trading software after all.

Hope this helps.