Skip to content

Instantly share code, notes, and snippets.

@darscan
Created June 27, 2013 16:00
Show Gist options
  • Save darscan/5877710 to your computer and use it in GitHub Desktop.
Save darscan/5877710 to your computer and use it in GitHub Desktop.
Flex-like Invalidator
package robotlegs.bender.framework.impl
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.IEventDispatcher;
public class Invalidator
{
private var commit:Function;
private var ticker:IEventDispatcher;
private var trigger:String;
private var listening:Boolean;
private var invalidating:Boolean;
public function Invalidator(
commitFunction:Function,
dispatcher:IEventDispatcher = null,
eventType:String = "")
{
commit = commitFunction;
ticker = dispatcher || new Sprite();
trigger = eventType || Event.EXIT_FRAME;
}
public function invalidate():void
{
invalidating = true;
if (!listening)
{
listening = true;
ticker.addEventListener(trigger, onTick, false, 0, true);
}
}
private function onTick(event:Event):void
{
// There is an optimization here that leaves the listener attached
// for one extra tick.
// This reduces the overhead of constantly attaching and removing
// the listener when invalidation occurs between ticks.
if (invalidating)
{
invalidating = false;
commit();
}
else
{
listening = false;
ticker.removeEventListener(trigger, onTick);
}
}
}
}
package robotlegs.bender.framework.impl
{
import flash.events.Event;
import flash.events.EventDispatcher;
import org.hamcrest.assertThat;
import org.hamcrest.object.equalTo;
public class InvalidatorTest
{
/*============================================================================*/
/* Private Properties */
/*============================================================================*/
private const EVENT_TYPE:String = "tick";
private var invalidator:Invalidator;
private var dispatcher:EventDispatcher;
private var counter:int;
/*============================================================================*/
/* Test Setup and Teardown */
/*============================================================================*/
[Before]
public function before():void
{
counter = 0;
invalidator = null;
dispatcher = new EventDispatcher();
}
/*============================================================================*/
/* Tests */
/*============================================================================*/
[Test]
public function commit_is_called_after_tick_when_invalidated():void
{
createInvalidator(incrementCounter);
invalidate();
tick();
assertThat(counter, equalTo(1));
}
[Test]
public function commit_is_not_called_until_tick():void
{
createInvalidator(incrementCounter);
invalidate();
assertThat(counter, equalTo(0));
}
[Test]
public function commit_is_called_once_per_tick():void
{
createInvalidator(incrementCounter);
invalidate();
tick();
tick();
assertThat(counter, equalTo(1));
}
[Test]
public function commit_is_called_once_per_invalidation():void
{
createInvalidator(incrementCounter);
invalidate();
invalidate();
tick();
assertThat(counter, equalTo(1));
}
[Test]
public function commit_is_called_for_each_invalidation():void
{
createInvalidator(incrementCounter);
invalidate();
tick();
invalidate();
tick();
assertThat(counter, equalTo(2));
}
[Test]
public function optimisation___handler_is_only_added_once_between_ticks():void
{
dispatcher = new CallbackDispatcher().whenAddingListener(incrementCounter);
createInvalidator(nop);
invalidate();
invalidate();
assertThat(counter, equalTo(1));
}
[Test]
public function optimisation___handler_is_NOT_removed_until_a_non_invalidated_tick_has_passed():void
{
dispatcher = new CallbackDispatcher().whenRemovingListener(incrementCounter);
createInvalidator(nop);
invalidate();
tick();
invalidate();
tick();
assertThat(counter, equalTo(0));
}
[Test]
public function optimisation___handler_IS_removed_after_a_non_invalidated_tick_has_passed():void
{
dispatcher = new CallbackDispatcher().whenRemovingListener(incrementCounter);
createInvalidator(nop);
invalidate();
tick();
tick();
assertThat(counter, equalTo(1));
}
/*============================================================================*/
/* Private Functions */
/*============================================================================*/
private function createInvalidator(commitFunction:Function):void
{
invalidator = new Invalidator(commitFunction, dispatcher, EVENT_TYPE);
}
private function invalidate():void
{
invalidator.invalidate();
}
private function tick():void
{
dispatcher.dispatchEvent(new Event(EVENT_TYPE));
}
private function incrementCounter():void
{
counter++;
}
private function nop():void
{
}
}
}
import flash.events.EventDispatcher;
class CallbackDispatcher extends EventDispatcher
{
/*============================================================================*/
/* Private Properties */
/*============================================================================*/
private var _addEventListenerCallback:Function;
private var _removeEventListenerCallback:Function;
/*============================================================================*/
/* Public Functions */
/*============================================================================*/
public function whenAddingListener(callback:Function):CallbackDispatcher
{
_addEventListenerCallback = callback;
return this;
}
public function whenRemovingListener(callback:Function):CallbackDispatcher
{
_removeEventListenerCallback = callback;
return this;
}
override public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
{
_addEventListenerCallback && _addEventListenerCallback();
super.addEventListener(type, listener, useCapture, priority, useWeakReference);
}
override public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
{
_removeEventListenerCallback && _removeEventListenerCallback();
super.removeEventListener(type, listener, useCapture);
}
}

Basic Usage

In some class:

MyClass
{
  private const invalidator:Invalidator = new Invalidator(commitProperties);
  
  protected function invalidateProperties():void {
    invalidator.invalidate();
  }
  
  protected function commitProperties():void {
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment