This post and code is related to my fork of as3-signals. The fork is from the latest version of as3-signals 0.9-BETA.
Summary of additions
- ISlot.applyTo(value:*):void
- ISlot.doesApply(value:*):Boolean
- SlotList.filterNotAppliesTo(value:*):SlotList
- SlotList.findNotAppliesTo(value:*):SlotList
Summary of modifications
- IOnceSignal.removeAll(appliesTo:* = null):void
Details
This backwards compatible addition to as3-signals chiefly allows the removal of all listeners for a given instance or class type. It keeps the convenience of the removeAll function but provides a mechanism to prevent removal of listeners not bound to the current instance or class.
The removeAll() method allows for an optional parameter which will then removeAll slots that applyTo the given parameter, if any.
Essentially, this allows us to asynchronously clean up after a class or instance without affecting any listeners from other classes and without having to define our anonymous functions or closures.
Reasoning
A signal – as a promise – may be reused across instances and classes. For example, a service that is managed by an IOC container may cache signal calls, or a model may use signals to notify of changes. When the signal is shared across various types (either directly via the IOC container, or indirectly cached within an instance) we don’t want to removeAll() lest we remove functional code in other classes (that may well have been written by other developers).
Example
Update Sept 23: Changed the example to use Robotlegs and Mediation
Let’s look at an example to follow this through. This example below illustrates usage when marshalling updates of a model to a view (using covariant mediation to an interface) via a Mediator. Chiefly, the problem arises when an asynchronous cleanup method is called (in this case `onRemove()`), it needs to remove all of the listeners applied to by this instance but not remove listeners that may be used elsewhere.
public class LogModel
{
protected var updateSignal:ISignal;
protected var logs:Vector.<LogItem>;
public function get update():ISignal
{
return updateSignal ||= new Signal();
}
public function set logs(collection:Vector.<LogItem>):void
{
logs = collection;
update.dispatch(logs);
}
}
public class SomethingMediator extends Mediator
{
[Inject]
public var model:LogModel;
[Inject]
public var view:IDoesSomething;
override public function onRegister():void
{
model.update.add(function(collection:Vector.<LogItem>):void
{
view.logs = collection;
}).applyTo(this);
}
override public function onRemove():void
{
model.update.removeAll(this); //removes all listeners applied to this instance only
}
}
Usage (pseudo-code):
//setup values const logModel:LogModel = new LogModel(); injector.mapValue(LogModel, logModel); //map interfaces to a mediator mediatorMap.mapMediator(IDoesSomething, SomethingMediator); const viewA:IDoesSomething = new DoesSomething(); const viewB:IDoesSomething = new DoesSomething(); //register mediators for our views (creates two instances of SomethingMediator) mediatorMap.registerMediators(viewA); mediatorMap.registerMediators(viewB); trace(logModel.update.numListeners); //2 - one listener from viewA.onRegister() and one from viewB.onRegister() mediatorMap.removeMediators(viewA); trace(logModel.update.numListeners); //1 - one listener from viewB.onRegister() mediatorMap.removeMediators(viewB); trace(logModel.update.numListeners); //0













In this example, shouldn’t you use addOnce()?
Robert,
I concede it’s not the best example, but I wanted to keep it brief.
However, addOnce() only removes the listener once dispatched, so if then the containing “Thing” instance was disposed before the signal dispatched we’d still have listeners waiting for something that should have been disposed.
Justin
Also, if I used addOnce(), readers may have been confused why I’d bother cleaning up at all and then I’d have had to explain the point above. =)
To me, the example is extremely important. To add API, there needs to be a compelling use case.
In this scenario, the service class should be removing the signal’s listeners every time a transaction completes or times out. Then there isn’t anything left for your feature to do.
Robert – I’ve updated the example to use Robotlegs. It now pushes shared model changes onto the views for each mediator.
The update signal from the model is exposed to the Mediator however we obviously don’t want to remove all listeners from the shared signal. As such, we use applyTo() to ensure that we can add one or more handlers within an instance and removeAll in one fell swoop without effecting other handlers in other classes/instances.