Skip to content

Commit

Permalink
create lightweight TinyEmitter, Emitter runs on TinyEmitter, #222 phe…
Browse files Browse the repository at this point in the history
  • Loading branch information
zepumph committed Apr 12, 2019
1 parent a9c6bbc commit 753d0f1
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 46 deletions.
114 changes: 114 additions & 0 deletions js/Emitter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright 2015-2019, University of Colorado Boulder

/**
* Event & listener abstraction for a single "event" type. The type provides extra functionality beyond just notifying
* listeners. It adds PhET-iO instrumentation capabilities as well as validation. For the lightest-weight, fastest
* solution with the smallest memory footprint, see `TinyEmitter`.
*
* @author Sam Reid (PhET Interactive Simulations)
* @author Michael Kauzmann (PhET Interactive Simulations)
*/
define( require => {
'use strict';

// modules
const Action = require( 'AXON/Action' );
const axon = require( 'AXON/axon' );
const TinyEmitter = require( 'AXON/TinyEmitter' );

class Emitter extends Action {

/**
* @param {Object} [options]
*/
constructor( options ) {

// TODO https://github.com/phetsims/axon/issues/222: clean this up
// options = _.extend( {
//
// // phetioType: EmitterIOWithNoArgs // subtypes can override with EmitterIO([...]), see EmitterIO.js
// }, options );

super( null, options );

const self = this;

// @private - provide Emitter functionality via composition
this.tinyEmitter = new TinyEmitter();

// Set the action in the parent type now that we have self, use function to support arguments
this.action = function() {
self.tinyEmitter.tinyEmit.apply( self.tinyEmitter, arguments );
};
}

/**
* Disposes an Emitter. All listeners are removed.
* @public
* @override
*/
dispose() {
this.tinyEmitter.dispose();
super.dispose();
}

/**
* Adds a listener which will be called during emit.
* @param {function} listener
* @public
*/
addListener( listener ) {
this.tinyEmitter.addListener( listener );
}

/**
* Removes a listener
* @param {function} listener
* @public
*/
removeListener( listener ) {

this.tinyEmitter.removeListener( listener );
}

/**
* Removes all the listeners
* @public
*/
removeAllListeners() {

this.tinyEmitter.removeAllListeners();
}

/**
* Checks whether a listener is registered with this Emitter
* @param {function} listener
* @returns {boolean}
* @public
*/
hasListener( listener ) {
return this.tinyEmitter.hasListener( listener );
}

/**
* Returns true if there are any listeners.
* @returns {boolean}
* @public
*/
hasListeners() {
return this.tinyEmitter.hasListeners();

}

/**
* Returns the number of listeners.
* @returns {number}
* @public
*/
getListenerCount() {
return this.tinyEmitter.getListenerCount();
}
}

return axon.register( 'Emitter', Emitter );
} );
76 changes: 30 additions & 46 deletions js/TinyEmitter.js
Original file line number Diff line number Diff line change
@@ -1,73 +1,57 @@
// Copyright 2015-2018, University of Colorado Boulder
// Copyright 2019, University of Colorado Boulder

/**
* Lightweight event & listener abstraction for a single event type.
*
* @author Sam Reid (PhET Interactive Simulations)
* @author Michael Kauzmann (PhET Interactive Simulations)
*/
define( require => {
'use strict';

// modules
const Action = require( 'AXON/Action' );
const axon = require( 'AXON/axon' );

// Simulations have thousands of Emitters, so we re-use objects where possible.
const EMPTY_ARRAY = [];
assert && Object.freeze( EMPTY_ARRAY );
class TinyEmitter {
constructor() {

// TODO https://github.com/phetsims/axon/issues/222: create LightweightEmitter which does not extend PhetioObject, but used here by composition

class Emitter extends Action {

/**
* @param {Object} [options]
*/
constructor( options ) {

// TODO https://github.com/phetsims/axon/issues/222: clean this up
// options = _.extend( {
//
// // phetioType: EmitterIOWithNoArgs // subtypes can override with EmitterIO([...]), see EmitterIO.js
// }, options );

super( null, options );

const self = this;

// Set the action in the parent type now that we have self, use function to support arguments
this.action = function() {

// Notify wired-up listeners, if any
if ( self.listeners.length > 0 ) {
self.activeListenersStack.push( self.listeners );

// Notify listeners--note the activeListenersStack could change as listeners are called, so we do this by index
const lastEntry = self.activeListenersStack.length - 1;
for ( let i = 0; i < self.activeListenersStack[ lastEntry ].length; i++ ) {
self.activeListenersStack[ lastEntry ][ i ].apply( null, arguments );
}

self.activeListenersStack.pop();
}
};

// @private {function[]} - the listeners that will be called on emit
// @private {function[]} - the listeners that will be called on tinyEmit
this.listeners = [];

// @private {function[][]} - during emit() keep track of which listeners should receive events in order to manage
// @private {function[][]} - during tinyEmit() keep track of which listeners should receive events in order to manage
// - removal of listeners during emit()
this.activeListenersStack = [];
}

/**
* Disposes an Emitter. All listeners are removed.
* @public
* @override
*/
dispose() {
this.listeners.length = 0; // See https://github.com/phetsims/axon/issues/124
super.dispose();
}

/**
* Notify listeners
*
* This is called "tinyEmit" because in javascript polymorphism is difficult because of string searches, and we
* didn't want confusion between this call and Emitter.emit().
* @public
*/
tinyEmit() {

// Notify wired-up listeners, if any
if ( this.listeners.length > 0 ) {
this.activeListenersStack.push( this.listeners );

// Notify listeners--note the activeListenersStack could change as listeners are called, so we do this by index
const lastEntry = this.activeListenersStack.length - 1;
for ( let i = 0; i < this.activeListenersStack[ lastEntry ].length; i++ ) {
this.activeListenersStack[ lastEntry ][ i ].apply( null, arguments );
}

this.activeListenersStack.pop();
}
}

/**
Expand Down Expand Up @@ -176,5 +160,5 @@ define( require => {
}
}

return axon.register( 'Emitter', Emitter );
return axon.register( 'TinyEmitter', TinyEmitter );
} );

0 comments on commit 753d0f1

Please sign in to comment.