Mondjuk készítünk egy osztályt ami például elemek hozzáadásakor vagy vagy valamilyen változáskor egy eseményt dob aminek hatására nekünk grafikusan is reprezentálnunk kell azokat. Vagy akár változások után intenzív számításokat kell végeznünk akár egy Bitmap-ot pixelenként újrarajzolni vagy objektumokat mozgatni méretezni. Hogyha nagyon rövid időn belül sok ilyen esemény is dobódhat nem gazdaságos ha minden egyes változás után azonnal elvégezzük az intenzív műveleteket. Erre a problémára lehet megoldás ha tároljuk az azonos függvényhívások legutolsó paramétereit és csak az ENTER_FRAME esemény bekövetkeztekor adjuk tovább a valóban meghívni kívánt metódusnak.
A problémára lehet egy megoldás az alábbi kód.

utils/EnterFrameCaller.as

/**
 * @author Reider
 */
package utils
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.utils.Dictionary;

	public class EnterFrameCaller
	{
		protected var enterSprite:Sprite
		protected var callHolder:Dictionary
		protected var callQue:ScopeQue

		public function get hasCallable():Boolean
		{
			return Boolean(callQue.length)
		}

		//--------------------------------------------------------------------------
		//
		//  Constructor
		//
		//--------------------------------------------------------------------------

		public function EnterFrameCaller()
		{
			callHolder = new Dictionary()
			callQue = new ScopeQue()
			enterSprite = new Sprite()
			enterSprite.addEventListener(Event.ENTER_FRAME, onEnterFrame)
		}

		//--------------------------------------------------------------------------
		//
		//  Public Methods
		//
		//--------------------------------------------------------------------------

		public function call(func:Function, args:Array):void
		{
			var scope:CallScope = getCallScope(func)
			scope.callLater(args)

			callQue.offer(scope)
		}

		public function abortCall(func:Function):void
		{
			var scope:CallScope = getCallScope(func, false)
			if(scope){
				scope.abort()
				callQue.remove(scope)
			}
		}

		public function remove(func:Function):void
		{
			var scope:CallScope = getCallScope(func, false)
			if(scope){
				scope.abort()
				callQue.remove(scope)
			}
			delete callHolder[func]
		}

		public function callNow():void
		{
			while(callQue.length){
				callQue.poll().callNow()
			}
		}

		//--------------------------------------------------------------------------
		//
		//  Protected Methods
		//
		//--------------------------------------------------------------------------

		protected function getCallScope(func:Function, createIfNotExists:Boolean=true):CallScope
		{
			var scope:CallScope = callHolder[func]
			if(!scope && createIfNotExists){
				scope = new CallScope(func)
				callHolder[func] = scope
			}

			return scope
		}

		//--------------------------------------------------------------------------
		//
		//  Callback Methods
		//
		//--------------------------------------------------------------------------

		protected function onEnterFrame(event:Event):void
		{
			callNow()
		}

	}
}
//--------------------------------------------------------------------------
//
//  Internal ScopeQue
//
//--------------------------------------------------------------------------
import flash.utils.Dictionary;

internal class ScopeQue
{
	private var map:Dictionary = new Dictionary()
	private var _length:uint = 0

	public function get length():uint
	{
		return _length
	}

	public function offer(scope:CallScope):void
	{
		if(map[scope] == null){
			map[scope] = scope
			_length++
		}
	}

	public function remove(scope:CallScope):void
	{
		if(map[scope] != null){
			delete map[scope]
			_length--
		}
	}

	public function poll():CallScope
	{
		for each(var scope:CallScope in map){
			remove(scope)
			return scope
		}

		return null
	}
}

//--------------------------------------------------------------------------
//
//  Internal CallScope
//
//--------------------------------------------------------------------------
internal class CallScope
{
	private var targetFunction:Function
	private var callLaterFlag:Boolean
	private var callArgs:Array

	public function CallScope( targetFunction:Function)
	{
		this.targetFunction = targetFunction
	}

	public function abort():void
	{
		callLaterFlag = false
	}

	public function callLater(args:Array):void
	{
		callLaterFlag = true
		callArgs = args
	}

	public function callNow():void
	{
		if(callLaterFlag){
			targetFunction.apply(null, callArgs)
			callArgs = null
			callLaterFlag = false
		}
	}
}

A kód Dictionary-ben tárolja a bizonyos függvényekhez tartozó argumentumokat és hogy volt-e hívás. Amikor meghívunk egy függvényt ha nem létezik hozzá tartozó adat tároló készít egyet ha igen lekéri a létezőt beállítja a paramétereket és a következő ENTER_FARAME-kor meghívja a függvényt a legfrissebb paraméterekkel. Aki jártas Flex-ben tudja hogy ott is hasonló technikát alkalmaznak a túl sok számítás elkerülésere. A kód nem igényli hogy display listához adják mivel kihasználja hogy az azon kívüli DisplayObject-ek is küldik a szükséges ENTER_FARAME eseményt.

Használatának szemléltetéséhez készítsünk egy osztályt ami elemeket tárol és új hozzáadásakor küld egy eseményt.

ItemHolderModel.as

package
{
	import flash.events.Event;
	import flash.events.EventDispatcher;

	[Event(name="change", type="flash.events.Event")]

	public class ItemHolderModel extends EventDispatcher
	{
		protected var items:Array

		public function get itemCount():int
		{
			return items.length
		}

		public function ItemHolderModel()
		{
			items = new Array()
		}

		public function addItem(item:*):void
		{
			items.push(item)

			dispatchEvent(new Event(Event.CHANGE))
		}

		public function getItem(index:int):*
		{
			return items[index]
		}
	}
}

A Main osztály ami szemlélteti a működésüket a következő.

EnterFarameCallerTut.as

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.utils.setInterval;

	import utils.EnterFrameCaller;

	public class EnterFarameCallerTut extends Sprite
	{
		private var enterCaller:EnterFrameCaller
		private var itemHolderModel:ItemHolderModel

		public function EnterFarameCallerTut()
		{
			enterCaller = new EnterFrameCaller()
			itemHolderModel = new ItemHolderModel()

			itemHolderModel.addEventListener(Event.CHANGE, onItemsChange)

			addItems(5)
			setInterval(addItems, 3000, 5)
		}

		private function addItems(num:uint):void
		{
			for(var i:int=0; i<num; i++){
				itemHolderModel.addItem("Elem "+i)
			}

			if(itemHolderModel.itemCount >= 20){
				enterCaller.abortCall(doSomeRedrawing)
			}
		}

		/*
			A caller ezt a függvényt hívja meg ami az intenzív
			számításokat fogja végezni
		*/
		private function doSomeRedrawing(name:String, count:uint):void
		{
			trace("doSomeRedrawing()", name, count)
		}

		/*
			Ez a függvényünk hívódik meg minden elem hozzáadás után
		*/
		private function onItemsChange(event:Event):void
		{
			trace("onItemsChange()")

			//Itt adhatjuk át a meghívandó függvény nevét és a paramétereit egy tömbben
			enterCaller.call(doSomeRedrawing, ["Item count", itemHolderModel.itemCount])
		}
	}
}

A fő osztályunk időközönként 5 elemet fog hozzáadni az elem tárolónkhoz de nincs arra szükségünk hogy minden egyes esemény után számításokat végezzünk ezért ahelyett, hogy direkt meghívnánk a kijelzésért vagy számításokért felelős függvényt az EnterFrameCaller-en keresztül fogjuk ezt megtenni ami majd továbbadja. Az addItems metódusban látható hogy van lehetőség a hívás megszakítására, a mintában a 20 elem elérése után már nem hívódik meg a feldolgozó függvény.
A kód pár másodperces futás után a következőt írja a konzolra:

onItemsChange()
onItemsChange()
onItemsChange()
onItemsChange()
onItemsChange()
doSomeRedrawing() Item count 5
onItemsChange()
onItemsChange()
onItemsChange()
onItemsChange()
onItemsChange()
doSomeRedrawing() Item count 10
onItemsChange()
onItemsChange()
onItemsChange()
onItemsChange()
onItemsChange()
doSomeRedrawing() Item count 15

Lehet látni hogy kiszűrte a felesleges frissítéseket és csak a szükséges időben hajtotta végre az intenzív számításokat szimuláló függvényünket.