Tutorials, extensions, and source files for ActionScript, Flash, and other Adobe products.

 

Order of Operations in ActionScript

Date October 30, 2009
Language ActionScript 3.0
Target Flash Player 9, Flash Player 10

Introduction

Understanding the order of operations in which actions occur within a system is important in being able to work effectively within that system. The execution of ActionScript in a SWF running in Flash Player follows a specific order that can be helpful in achieving your tasks correctly, and effectively.

This tutorial breaks down operation order of ActionScript into three parts: object life cycle, frame execution, and events. Frame execution is central to all of these, especially when considering timeline usage in Flash. It is the repeating process through which a SWF runs, creating objects, dispatching events and rendering the screen. Though much of the focus here is given to the timeline - as generated by the Adobe Flash Professional authoring tool (Flash Pro) - much of the content can still be applied to projects with a more ActionScript-centric approach.

Object Life Cycle

An object's life cycle represents the time at which it exists and is accessible in code, or visible through screen rendering. This process is pretty straightforward when it comes to objects created with ActionScript, so much of the focus here will be directed towards display objects on the timeline, where the timeline is in control of an object's life cycle.

Object Creation

The initialization of a SWF starts with (mostly) the document class constructor. This is the class associated with the main SWF instance, or the main timeline. Though this is the perceived entry point for a SWF application, the first code run within a SWF actually starts with any child objects that may be defined in the main timeline's first frame (as created in Flash Pro, or otherwise defined in SWF tags).

Objects on the timeline are constructed from the bottom up, starting with the children working their way up through to the parents until reaching the document class instance. The parents of these children will have to exist by this point, so Flash uses a 2-pass initialization process, one that initializes objects without construction, and one that runs the constructors on each of those objects created. The initialization phase builds the display list hierarchy with physical object instances. It also uses this time to copy over any class variables into those instances, but leaves the constructors alone (class variables meaning values defined with class member declarations within the class body). The constructors aren't run until the second pass, after this initial setup is complete. The constructors for all objects are executed, starting with child objects working up the hierarchy to the document instance. The basic order of operations for a timeline-based frame construction is:

Order of operations: Object Creation
  1. All display objects initialized
    1. Display list hierarchy created
    2. Class properties assigned
  2. Display objects constructed, children first, bottom up
    1. Parent instance variables set; DisplayObject.name defined
    2. Display list properties set (parent, stage, root, loaderInfo)
    3. Constructors called
    4. Event: DisplayObject.added
    5. Event: DisplayObject.addedToStage

This creates a condition that allows all objects, within their constructors, to access any other object in the display list hierarchy even though their constructors have not yet run. It will be guaranteed that instances that are within an object's own display list have been constructed when its constructor runs since children are constructed first.

Note: Objects are added to the display list in the initialized phase even though their added/addedToStage events don't occur until their construction phase, immediately after the constructor.

The following illustrates the construction order of the first frame of a SWF given a collection of children and grandchildren, where objects lower in the list are at a lower depth (i.e. child2 is below child1, etc.):

  • stage
    • root (8)
      • child1 (7)
        • grandchild1 (6)
        • grandchild2 (5)
      • child2 (4)
        • grandchild1 (3)
        • grandchild2 (2)
      • child3 (1)

If you are managing all instance instantiation through code, you will have complete control over instantiation order. And in that case, the document class constructor will, in fact, be the entry point for the application, running before any other constructors do since it is from within that constructor other objects will be created. The added and addedToStage events will also then coincide directly with calls to addChild()/addChildAt().

Object Destruction

Objects generally cease to exist, or are removed from memory, when they a) are no longer able to be referenced through ActionScript and b) are cleaned up by garbage collector (GC) process in Flash Player. The GC is a hidden, background process that automatically manages memory for you, deleting objects in memory when they're no longer being used. References to timeline objects are automatically managed by the timeline, created and removed as the frames with the timeline objects are entered and exited.

When a timeline object no longer exists in the next frame, it will be destroyed at the end of the current frame. There are no destructors in ActionScript, so display object destruction is often identified by removal from the timeline which can be captured through events. This process will usually mean removal from memory as well unless any custom ActionScript references persist after this process.

Removal from the timeline occurs only for the top-most parent of a given display list. In other words, an object removed from the timeline doesn't require that the object's own display list be disassembled.

The order of the operations for an object being destroyed from the timeline is as follows:

Order of operations: Object Destruction
  1. Display objects removed from display list, bottom up (Top-most parent of removed display list only)
  2. Event: DisplayObject.removed (Top-most parent of removed display list only)
  3. Event: DisplayObject.removedFromStage
    1. Event: DisplayObject.removedFromStage, parents first, bottom up
  4. Parent instance name variable nulled
  5. Display list properties nulled (parent, stage, root, loaderInfo)
  6. Display object destroyed by the GC (Pending any lingering references)

Notice that, at least in terms of the events, objects are removed starting with the parents rather than with the children as is the case with construction. In both cases this order is still bottom up, starting with those instances lowest in depth. Though only the top-most parent is removed from the active display list, all descendents in display list getting removed will get a removedFromStage event, and that starts with parents first which then propagate this event to their children. The children do not get their own removed event.

More importantly be aware that physical removal from the display list does occur before these events take place. Though a display object's display list properties (parent, stage, etc.) are still accessible for reference, operations like parent.getChildIndex(this) may fail in a removed event because the object no longer physically exists within its parent at this point (for those objects that get a removed event). The existence of valid values for properties like parent is for convenience only, not to indicate presence within the display list. Note that this applies to timeline-based removal only. When using ActionScript to remove display objects, the REMOVED event occurs before physical removal of the child from the display list.

Null Timeline References

Flash Pro works with Flash Player to make sure timeline objects can be referenced easily through code. When screen objects are given instance names in Flash Pro, that causes two things to happen, 1) a member variable is created with that name in the class definition of the containing timeline object (this option can be turned off in the publish settings) and 2) the name property of the timeline instance is assigned the value of the instance name as a string. The member variable is assigned in construction and nulled in destruction.

Though you shouldn't be doing this anyway, should you happen to have two instances on a timeline that share the same instance name and share some, but not all the same frames, you'll have a situation where the timeline member variable used to reference that object will be nulled even though that object (one of them) may still exist on screen. This is because the destruction process does not concern itself with the current state of a frame, taking into account any object that may still exist in that frame with the same name. It simply nulls the member variable reference when any instance of that name is removed from the timeline. This is an edge case but I've seen it happen before.

The following illustrates the order of removal given the assumption that the main timeline (root) is moving to a completely empty frame. This also corresponds to the removed event:

  • stage
    • root (Never removed)
      • child1 (3)
        • grandchild1
        • grandchild2
      • child2 (2)
        • grandchild1
        • grandchild2
      • child3 (1)

Remember, only the top-most display object in a display list is removed. That object's own, now orphaned display list remains intact. The following is the same example from above but with respect to the order of the removedFromStage event:

  • stage
    • root (Never removed)
      • child1 (5)
        • grandchild1 (7)
        • grandchild2 (6)
      • child2 (2)
        • grandchild1 (4)
        • grandchild2 (3)
      • child3 (1)

You can see here how the removal process starts with the parents rather than the children as in construction.

Note: The addedToStage and removedFromStage events weren't in the first release of Flash Player 9, instead starting with Flash Player 9.0.28.0 - the version of Flash Player that shipped with Adobe Flash CS3 Professional.

Frame Execution

Frames are the heart of a running SWF. There are actually two concepts of frames in Flash. One is a visual frame, and the other a measure of time. Visual frames are individual "frame" segments depicted in a timeline seen in Flash Pro. These frames represent visual states which, during playback, are run in sequential order (unless instructed to do otherwise) matching frame time.

The other frame concept relates a frame to a measure of time. This time is determined by the frame rate of a SWF. A common frame rate is 24 frames per second. This creates a frame time of 1/24th of a second meaning every 1/24th of a second, Flash go to and render the next frame in a timeline. If there is only one frame in a timeline, that same, single frame is rendered every 1/24th of a second.

Timelines can have script associated with individual frames. These are known as frame scripts. In the context of Flash Pro, frame scripts serve two roles. First, they help define the class definition for the timeline's instance, adding any member variables or functions to that definition when defined in the frame script. Secondly, actions specific to that frame become wrapped up in a callback used with an undocumented MovieClip.addFrameScript() method. These callbacks get called when the timeline frame is first played.

ActionScript is executed repeatedly with the frame time through the DisplayObject.enterFrame event. This event is designed specifically to loop with the frame time, irrespective of the playback state or progression of any timelines; it does not behave like frame scripts which only run when the playhead enters a new timeline frame. The term "frame" in enterFrame is specific to frame time. This can be confusing, especially when compared to other similar events that have been added in Flash Player 10, DisplayObject.frameConstructed and DisplayObject.exitFrame. These are much like enterFrame in that they occur every frame, but these events also relate to changes in the playhead and can in fact be called multiple times a frame if the playhead changes via ActionScript.

Because of the enterFrame event's timing, it is the standard event used for any kind of ActionScript that should repeat with the frame time and rendering. An unfortunate consequence of the enterFrame event is that it occurs before new timeline objects have been created (despite places being made for them in the display list). This is what the frameConstructed event was intended to fix.

One last frame event is the DisplayObject.render event. This event is fired when Flash Player is about to render the screen. This happens at least once a frame, sometimes more. A unique characteristic of the render event is that it will only be called if a call has been made to stage.invalidate() since the last render. The render event will also not be called for display objects not on the display list, which is not the case for events like enterFrame.

A summary of the order of operations of a frame (as dictated by the value of MovieClip.currentFrame) is as follows. Operations marked with FP10+ apply only to Flash Players version 10 or greater.

Order of operations: Frame
  1. Current frame timeline objects not on last frame declared; numChildren updated
  2. Event: DisplayObject.enterFrame (New frame objects are still null)
  3. Current frame timeline objects not on last frame created (See Object Creation)
  4. Event: DisplayObject.frameConstructed (FP10+)
  5. Frame scripts/MovieClip.addFrameScript() callbacks, parents first, bottom up (Assuming this is a new frame in the timeline)
  6. Event: DisplayObject.exitFrame (FP10+)
  7. Event: DisplayObject.render (Assuming stage.invalidate() has been called)
  8. Scheduled screen render for frame
  9. Idle
    1. User interaction events such as InteractiveObject.click, InteractiveObject.keyUp, etc.
    2. Possible update renders
  10. Current frame timeline objects not on next frame destroyed (See Object Destruction)

An important part of this series of operations not mentioned previously is the idle step. This is the point in time where the player waits for the next frame iteration as specified by the frame rate. It is also the point in time where it processes user input events from the OS such as mouse and keyboard events. ActionScript within the handlers of these user input events have the option of calling MouseEvent.updateAfterEvent()/KeyboardEvent.updateAfterEvent() to force an additional render directly after the execution of the handler. Additionally, some of these events will automatically re-render the player. Mouse out and over events, for example, will re-render the screen to ensure that the visual states for buttons are immediately drawn rather having to wait for the next scheduled frame render.

Flash Player 10 EnterFrame Initializer Hack

It is an inconvenience that new timeline objects do not exist in the enterFrame event. However, in Flash Player 10, you can explicitly force a frame to construct itself within an enterFrame event to get around the null timeline instances in that frame. This is achieved simply by calling:

gotoAndPlay(currentFrame);

from within the enterFrame handler. This will invoke the navigation stack which includes a full initialization - with construction - of that frame. Unfortunately this does not work for Flash Player 9.

Note: This behavior is a new to Flash with ActionScript 3.0. ActionScript 2.0 constructed objects before the enterFrame (onEnterFrame) event.

Inter-frame Navigation

Standard SWF playback runs through timeline frames in sequential order. At any point in time in ActionScript, navigation commands (gotoAndPlay(), nextFrame(), etc.) can be used to change the current frame or state of playback. When this occurs, so does a subset of the standard frame process as indicated below.

Order of operations: Frame Navigation
  1. Current frame timeline objects not on new frame destroyed (See Object Destruction)
  2. Current frame timeline objects not on last frame created (SWF10+, See Object Creation)
  3. Event: DisplayObject.frameConstructed (SWF10+)
  4. Frame scripts/MovieClip.addFrameScript() callbacks (SWF10+, Assuming this is a new frame in the timeline)
  5. Event: DisplayObject.exitFrame (SWF10+)

Note: DisplayObject.enterFrame is not dispatched as a result of frame navigation; it always remains in sync with the frame rate and standard frame rendering. Rendering also does not occur as a direct result of frame navigation.

SWFs published targeting Flash Player 10 or greater (SWF10+) do a little more work during navigation, including executing frame scripts and constructing objects immediately after navigating to the new frame. This in turn also invokes the frameConstructed and exitFrame events - but only for 10 version SWFs. For Flash Player 9 SWFs, these operations are deferred until the next frame cycle. In fact, when it comes down to it, for Flash Player 9, the only thing that happens when going to a new frame is the removal of the old frame's objects.

Versioning: Flash Player vs. SWF

It's important to recognize and understand the difference between versioning with things like Flash Player 10 SWFs and SWFs running in Flash Player 10 (FP10). There are two versions at play here, one of the SWF itself and one of the player playing the SWF. The Flash Player version determines what features the player is capable of. The SWF version indicates the compatibility mode the SWF should be run in when running in Flash Player.

Some features/behaviors of Flash Player are version checked to only work with certain (typically newer) SWF versions. This is the case with new object creation and frame script execution with respect to frame navigation as seen above. This behavior is version checked in Flash Player 10 to determine which behavior to use, the SWF9 behavior or the SWF10+ behavior.

Some features like the frameConstructed and exitFrame events in standard frame playback are not version checked and will work in all SWF versions just so long as they're played in Flash Player 10 or later. A Flash Player 9 SWF should not rely on Flash Player 10 features like this, but they can sometimes be useful to take advantage of even if only on an if-available basis. Most new features, and even bug fixes for that matter, are version checked to make sure older SWFs run as designed.

The time at which the execution of these new frame steps occur can depend on when the navigation occurred. Specifically, all frame navigation for an instance occurring within the frame scripts of that instance is queued and executed at the end of the call stack. This means the frame does not change directly after a navigation call is made.

// frame script
trace(currentFrame); // 1
gotoAndStop(5);
trace(currentFrame); // 1

This does not apply to other movie clip instances or events such as enterFrame.

// frame script
trace(child.currentFrame); // 1
child.gotoAndStop(5);
trace(child.currentFrame); // 5
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
function enterFrameHandler(event:Event):void {
	trace(currentFrame); // 1
	gotoAndStop(5);
	trace(currentFrame); // 5
}

The purpose of this behavior is to ensure scripts tied to frames remain in the context of their frame for the entirety of their execution.

Note: This queuing behavior is a new to Flash with ActionScript 3.0. ActionScript 2.0 allowed frame scripts to change frames during their execution.

SWF9 Navigation Bug

Flash Player 9 SWFs suffers from many timeline bugs, including double-frame playback with goto commands, skipping first-frame frame scripts in certain situations, lingering timeline references, as well as a number of others. But probably the most bothersome (subjectively, of course) is the poor management of objects within navigation commands. And this actually relates to multiple issues, but each of which should be solvable with a common solution: deferring frame navigation to enterFrame events.

var gotoFrame:int = 0; // frame to navigate

// handler for accepting user action that
// results in a new frame
button.addEventListener(MouseEvent.CLICK, clickHandler);
function clickHandler(event:MouseEvent):void {
	// instead of gotoAndPlay(5);
	gotoFrame = 5; // specify goto frame
}

// poller detecting new frame navigation
// which is handled in the enterFrame event
addEventListener(Event.ENTER_FRAME, navigatorHandler);
function navigatorHandler(event:Event):void {
	if (gotoFrame){
		// go to goto frame
		gotoAndPlay(gotoFrame);
		gotoFrame = 0; // reset
	}
}

You may be wondering why is Flash Player 9 is so bad off. This stems from the fact that ActionScript 3.0, which was introduced in Flash Player 9, was designed specifically with Flex in mind. Flex doesn't use the timeline - extensively, at least - so a lot of issues slipped through. Most of these issues have been addressed in Flash Player 10, version checked for SWF10.

Events

Events are dispatched from individual objects from their own, individual event queues. Event listener functions are added to these queues through EventDispatcher.addEventListener(). By default, listeners added first will get called first. This behavior can be influenced through the priority parameter of addEventListener. The higher the value of priority, the closer to the beginning of the existing event queue the listener is added. Listeners added with the same priority will follow the order of being added. For example if two listeners are added with an event priority of 10, the first of those listeners added will get called first.

// in a display object:
addEventListener(Event.ENTER_FRAME, handler1); // called third 
addEventListener(Event.ENTER_FRAME, handler2); // called fourth
addEventListener(Event.ENTER_FRAME, handler3, false, 10); // called first
addEventListener(Event.ENTER_FRAME, handler4, false, 10); // called second

Broadcast Events

Any event that targets multiple objects at once is known as broadcast event. These events are managed through a dispatcher queue - one operated internally by Flash Player. This queue is used to keep track of all of the objects that need to dispatch events for a certain event type. For example, the first time a display object is given a listener for the broadcast event enterFrame, that object is added to an enterFrame dispatcher queue. All objects in this queue dispatch the enterFrame event. If at any point in time that object has no enterFrame listeners, it is removed from that queue. After all, there's no reason to dispatch an event if no listeners are there to listen for it. This queue is important because it also dictates event order. Specifically, for events like enterFrame, the order in which objects within a SWF get the enterFrame event is dictated by the point in time when they receive their first enterFrame event listener.

// in a display object:
this.addEventListener(Event.ENTER_FRAME, handler1); // called first
stage.addEventListener(Event.ENTER_FRAME, handler2); // called third
this.addEventListener(Event.ENTER_FRAME, handler3); // called second

In the code sample above, because stage was given its first enterFrame listener after this was, its listeners won't get called until all of the listeners added to this have fired. The priority parameter will have no affect on this since it only applies to an individual object's listener set.

// in a display object:
this.addEventListener(Event.ENTER_FRAME, handler1); // called first
stage.addEventListener(Event.ENTER_FRAME, handler2, false, 10); // still called third
this.addEventListener(Event.ENTER_FRAME, handler3); // still called second

The only way the stage handler would be able to be called before the this handlers is if the handlers on this were all removed, and then re-added. This would remove the this instance from the enterFrame event queue, then re-add it after stage once the listeners were replaced.

Other broadcast events include DisplayObject.activate, DisplayObject.frameConstructed, and DisplayObject.render, among others. Each event that is a broadcast event should be marked as being so in the ActionScript language reference.

Event Bubbling

Events that bubble traverse a display list hierarchy to call listeners on multiple objects. These display objects include the source object from which the event originated (Event.target) as well as all of its parents (Event.currentTarget). This happens bidirectionally, going both down and up the display list, in what is described as three phases: capturing, at-target, and bubbling.

The capture phase happens first and starts with stage. From stage, the event makes its way through all parents of the source, or target, display object, calling all of the listeners in each of those objects' event queues if they exist. After capture is the at-target phase which consists solely of the target display object. After its handlers have been called, the event goes through bubbling phase, again calling all of the listeners of the parents, but now in the opposite order of the capturing phase ending at stage.

By default addEventListener only applies listeners to the bubbling and at-target phases (the distinction between those two phases depends on which objects you add the listeners to). To add listeners to the capturing phase, you need to set the useCapture flag in addEventListener to true.

stage.addEventListener(MouseEvent.CLICK, handler1); // called third (bubbling)
stage.addEventListener(MouseEvent.CLICK, handler2, true); // called first (capturing)
button.addEventListener(MouseEvent.CLICK, handler3); // called second (at-target)

Neither the order in which listeners are added to objects, nor the priority of listeners will have any bearing on the order of the progression of a bubbling event through the various event phases. This is dictated entirely by the display list hierarchy. Those characteristics still effect the order of multiple events attached to the same object within that hierarchy, though.

Event bubbling order by event phase:

Order of operations: Event Bubbling
  1. Capturing phase (useCapture = true)
    1. stage
    2. ...
    3. target.parent
  2. At-target phase (useCapture = false)
    1. target
  3. Bubbling phase (useCapture = false)
    1. target.parent
    2. ...
    3. stage

The number of display objects between the stage and the target display object can vary, of course. If the target object is the stage, there is only one phase, the at-target phase.

Bubbling events only apply to objects on the display list and only if the event is designed to bubble. Most of the events that do originate from user interaction such as click and keyUp events. As of Flash Player 10, no broadcast events also bubble.

Conclusion

Much of the information covered here may be beyond what can be considered need-to-know for your standard Flash projects. Nevertheless it can be useful in helping you understand and correctly manage application flow, especially when working with the timeline in Flash Pro. Even in situations where the timeline is not being used, the order of operations (which, without the timeline, mostly revolves around events), can become important to understand.

One thing not covered here is Flex. The Adobe Flex framework is ActionScript-based framework designed by Adobe to be used with Flash Player for developing applications. It has it's own event model and APIs that relate to different the operations that occur in that framework. And because Flex is based entirely on ActionScript, the way it handles the object life cycle differs from that of the standard Flash timeline. The order of Flex-based operations are out of the scope of this document, but some additional information about that can be found in the Flex (3) documentation on component instantiation life cycle.