RequiredSequence v0.4

Posted by Chuck Vose Fri, 02 Apr 2010 22:04:00 GMT

During my last mini flash project I created a project designed to help me with async calls in AS3. I hope that it helps you too. You can find the project on my github account.

Intro

Required Sequence is a tiny AS3 library that mandates that certain bits of code run before other bits of code. I’ve heard that Senocular has an excellent version of this but I could never quite grok his library. There’s also the apparently magical BulkLoader which you can find here. Before I found those two libraries I had already written my own and it works quite well for me.

Background

Required Sequence is essentially an implementation of the Publish/Subscribe design pattern in AS3. It relies on the principle that when an important configuration process finishes it will dispatch an Event on the main class. Other processes that are interested in this merely tell their Required Sequence instance to listen for that Event and execute some code when it happens.

For example, if you need to load an XML config file before doing anything else you could easily write something like this:


  rs.requireFlag(MyClass.CONFIG_LOADED,
    function () {
      // Do the rest of my program
    }
  );

Instructions

Build your RS listener as follows. This would be good to do for each class or each section of code but it must not die during execution until all bits of code are run.

Here is a complete running example of a program that requires the loadConfig function to be run before doing anything else.


package 
{
  import com.chuckvose.utils.RequiredSequence;

  public class MyClass
  {
    public static const CONFIG_LOADED = 'configLoaded';
    public var rs:RequiredSequence;

    public function MyClass()
    {
      rs = new RequiredSequence();

      // Ensure that we see the CONFIG_LOADED Event before doing anything else
      rs.requireFlag(MyClass.CONFIG_LOADED,
        function () {
          // Do the rest of my program
        }
      );
    }

    public function loadConfig()
    {
      // Do some stuff
      dispatchEvent(new Event(MyClass.CONFIG_LOADED));
    }
  }
}

Okay, that’s pretty cool, but does it do anything else? I’m glad you asked actually. The library does three other things right now that are really, really damned important; in fact, I would hazard that most people will only use these later functions but since they all descend from that somewhat simple use above it seemed like a good starting point.

Stubbornness

If you’ve ever dealt with URLLoaders you’ll know that sometimes they can be flakey. Wouldn’t it be cool if you could just write a bit of code to try a function n times once every m seconds until you get some signal?


  rs.requireFlagWithRetry(MyClass.CONFIG_LOADED,
    // Code that would be necessary to generate the interesting Event. You do
    // not need to invoke this manually, it will run this code immediately.
    function () {
      loadConfig();
    },
    // Code to run after we see the interesting Event.
    function () {
      // Do the rest of my program
    }
  );

Or if you want to more tightly control how many times and the spacing between attempts you could do this:


  var retries = 5; // Max number of tries
  var period = 2000; // Milliseconds between tries
  rs.requireFlagWithRetry(MyClass.CONFIG_LOADED,
    // Code that would be necessary to generate the interesting Event. You do
    // not need to invoke this manually, it will run this code immediately.
    function () {
      loadConfig();
    },
    // Code to run after we see the interesting Event.
    function () {
      // Do the rest of my program
    },
    retries,
    period
  );

Repetition

Naturally, being the subscriber/publisher pattern there will come a time when you want to listen repeatedly to something. For example, after I render something onto the canvas I usually want to refresh my scroll panes and hide any spinners that I’ve drawn, the following easily accommodates this desire.


  rs.requireFlagRepeatedly(MyClass.PANE_LOADED, function () {
    refreshCanvas();
    hideSpinner();
  });

Groups

Okay hot shit, what about if I just want to listen for a bunch of signals and I don’t really care about the order that they come in?


  rs.requireFlags([MyClass.CONFIG_LOADED, MyClass.LOCATION_LOADED], 
    function () {
      drawWeatherDisplay();
    }
  );

Manually

Okay, so maybe your desires don’t fall into one of the nice cases above, here’s what needs to happen in order for events to be registered. The heart is in the following 4 functions:

  • isComplete(flagName): checks to see if a flag has completed. Run this before you start listening just in case the Event has already fired for a previous listener.
  • addFlag(flagName): adds a flag to the list of flags to listen for. When we see an Event fire with this string in it we’ll check with all subscribers to see if that flag was interesting to them.
  • completeFlag/uncompleteFlag(flagName): after you run your code you probably want to completeFlag so that RS knows to not bother you anymore and delete your listener. Similarly, a standard part of the require* functions is to run completeFlag so you may want to uncompleteFlag if you want to mark an Event as not having run yet.
  • stopWatching/stopWatchingGroup: counterpart to addFlag these both tell RS to just stop listening for that flag or flags.

You can also look at the source, I hope that it’s easy enough to read.

Contact

If you have ideas for improvement you can always email me at vosechu+rs@gmail.com. Either that or just send me a pull request and I’ll probably rip it in lickety-split.

Thanks

Thank you to everyone that has ever sent me a message of encouragement. I really appreciate it; these are the fuel that Open Source developers thrive on.

Secondly, thank you to Metal Toad Media for letting me play with fun projects and paying me to develop outlandish little libraries like this.

Posted in ,  | Tags , , , ,  | 3 comments

Clearing the state of a actionscript 3 app (aka, globals are eeevil)

Posted by Chuck Vose Tue, 16 Feb 2010 22:53:00 GMT

Cross posted on the Metal Toad Blog

Imagination time: Imagine for a moment that you have an app, it has one button and one little window. When you click the button it changes the text within the window to something else. Now, when you get into flash development it seems like the easiest and clearest way to do this is to wipe out the contents in the window, but you would be wrong for thinking so. While it is the most obvious I intend to prove to you that to do so is both slower and prevents you from turning on the juice later with caching.

On to story time: We’re building an app for the OpenPeak tabletop device, it’s totally rad and you’ll love it I promise but that’s really not the point of the post. When we started the app we had what I considered a pretty awesome solution, we’d clear the state, build it onto a global, then write the global out to a window on the app. It actually worked great for a long time until we decided to start doing some caching where it became wildly apparent that I had actually written all three parts of that app dead wrong. It’s pretty exciting to do something wrong enough that you can write about it later. :)

Building on Globals (Horrible)

Okay, lets start with the most awful part of the lesson for me, that using globals is horrible and should never be done. Oh sure, they make certain listeners easier but it really does blow up fast when you start trying to cache things or thread. Here’s two scenarios that will inevitably bite you if you start using globals:

  • When you start threading you’ll end up painting two scenes on to the global stage object at once.
  • Even if you try to be synchronous you have to descend into an event listener hell since AS3 often decides to not dispatch Event.COMPLETE which means you’ll end up with a deadlocked app while a URLLoader tries in vane to do nothing.
  • At some point you’ll accidentally use the same URLLoader twice or some other critical variable and the results will be completely random seeming as one gets overwritten on occasion.

So, how can you deal with this? As annoying as it is, define your variables at the beginning of your functions. I know it seems like a waste, but unlike that one global TextFormat object your URLLoaders really are totally different beasts and need to be treated as such. Same goes for almost all objects and display containers. I know it seems nice and elegant to use the same object all the time but what you may not be seeing is that all those globals need to have IPC methods defined on them or they’re completely useless in the inherently async world that is AS3.

Clearing the State (Wrong)

This one is simple really, I should have thought of it to begin with it but I wasn’t in OO space yet. Rather than thinking about pages or DisplayObjects as something physical that needs to be removed from the stage it’s much, much more efficient to just build a new Sprite() and replace the old stage that you were using. Best part is that when you just replace the display container you’re using you can easily stash this away in a cache object somewhere. Simple caching is only moments away but if you wipe the slate clean not only did you spend time killing all those children objects, you lost the ability to cache it away somewhere.

Metaphorical you of course, my apologies.

So what we did was to use an Object (we should have used a Dictionary but I didn’t know any better yet) with attributes that described the path we were writing to. We never wrote to a global stage object (we actually deleted those so we couldn’t). This way async calls could continue to write to the correct Sprite even in the background, the only place we needed to put a lock around was the drawing part as we drew out the content but rather than drawing a global DisplayObject we took the data from the cache and drew that which meant it was much, much faster, handled multiple threads just fine, and was extremely cacheable.

Conclusion

I hope this gives you some ideas; my hope was merely to get people thinking about two things: global objects require IPC, and that wiping the slate clean is wasteful. Let the garbage collector take care of clearing memory and make sure that you have only a few global objects.

Next time I’m going to talk about how I got a requirements engine running which would allow me to ensure that certain steps were always completed before anything else without creating messy spaghetti code. I think it’s pretty fantastic and I hope that you do too.

Posted in ,  | Tags , , , , , ,  | 2 comments