             for each (layer in _layers) layer.drawFrame(newFrame);
         }
 
-        // Update the frame before firing, so if firing changes the frame, it sticks.
+        if (isGoTo) _playTime = newFrame / _frameRate;
+
+        // Update the frame before firing frame label signals, so if firing changes the frame,
+        // it sticks.
         const oldFrame :int = _frame;
         _frame = newFrame;
-        if (fromSkip) {
-            fireLabels(newFrame, newFrame);
-            _playTime = newFrame/_frameRate;
-        } else if (overDuration) {
-            fireLabels(oldFrame + 1, _frames - 1);
-            fireLabels(0, _frame);
-        } else if (differentFrame) {
-            if (wrapped) {
-                fireLabels(oldFrame + 1, _frames - 1);
-                fireLabels(0, _frame);
-            } else fireLabels(oldFrame + 1, _frame);
-        }
-        _goingToFrame = false;
+
+        // determine which labels to fire signals for
+        var startFrame :int;
+        var frameCount :int;
+        if (isGoTo) {
+            startFrame = newFrame;
+            frameCount = 1;
+        } else {
+            startFrame = (oldFrame + 1 < _numFrames ? oldFrame + 1 : 0);
+            frameCount = (_frame - oldFrame);
+            if (wrapped) frameCount += _numFrames;
+        }
+
+        // Fire signals. Stop if pendingFrame is updated, which indicates that the client
+        // has called goTo()
+        var frameIdx :int = startFrame;
+        for (var ii :int = 0; ii < frameCount; ++ii) {
+            if (_pendingFrame != NO_FRAME) break;
+
+            if (_labels[frameIdx] != null) {
+                for each (var label :String in _labels[frameIdx]) {
+                    labelPassed.dispatch(label);
+                    if (_pendingFrame != NO_FRAME) break;
+                }
+            }
+
+            // avoid modulo division by updating frameIdx each time through the loop
+            if (++frameIdx == _numFrames) {
+                frameIdx = 0;
+            }
+        }
+
+        _isUpdatingFrame = false;
+        // If we were interrupted by a goTo(), update to that frame now.
         if (_pendingFrame != NO_FRAME) {
             newFrame = _pendingFrame;
-            _pendingFrame = NO_FRAME;
-            updateFrame(newFrame, true, false);
-        }
-
-    }
-
-    /** @private */
-    protected function fireLabels (startFrame :int, endFrame :int) :void {
-        for (var ii :int = startFrame; ii <= endFrame; ii++) {
-            if (_labels[ii] == null) continue;
-            for each (var label :String in _labels[ii]) labelPassed.dispatch(label);
-        }
-    }
-
-    /** @private */
-    protected var _goingToFrame :Boolean;
+            updateFrame(newFrame, 0);
+        }
+    }
+
+    /** @private */
+    protected var _isUpdatingFrame :Boolean;
     /** @private */
     protected var _pendingFrame :int = NO_FRAME;
     /** @private */

                _stopFrame = NO_FRAME;
            }
        }
        updateFrame(newFrame, false, overDuration);

        for (var ii :int = 0; ii < this.numChildren; ++ii) {
            var child :DisplayObject = getChildAt(ii);


        // If _playTime is very close to _duration, rounding error can cause us to
        // land on lastFrame + 1. Protect against that.
        var newFrame :int = Math.min(int(_playTime * _frameRate), _numFrames - 1);

        // If the update crosses or goes to the stopFrame:
        // go to the stopFrame, stop the movie, clear the stopFrame
        if (_stopFrame != NO_FRAME) {
            // how many frames remain to the stopframe?
            var framesRemaining :int =
                (_frame <= _stopFrame ? _stopFrame - _frame : _numFrames - _frame + _stopFrame);
            var framesElapsed :int = int(actualPlaytime * _frameRate) - _frame;
            if (framesElapsed >= framesRemaining) {
                _playing = false;
