Last active
September 15, 2018 15:50
-
-
Save JCloudYu/a0bae5c38c5b4dd24b5b180b81806caa to your computer and use it in GitHub Desktop.
A tinny reimplementation of node-js styled event emitter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Author: JCloudYu | |
* Create: 2018/09/15 | |
**/ | |
((exports)=>{ | |
"use strict"; | |
const _previous = exports.EventEmitter; | |
const _EventEmitter = new WeakMap(); | |
class EventEmitter { | |
constructor() { | |
const PRIVATES = {}; | |
_EventEmitter.set(this, PRIVATES); | |
PRIVATES._event_queue = []; | |
PRIVATES._handle_promise = false; | |
} | |
/** | |
* Emit an event. Note that if handlePromise property is set to true, this function will return a promise or undefined otherwise. | |
* | |
* @param {string} eventName The name of event to be emitted | |
* @param {...*} args The arguments that are passed to the listeners | |
* @returns {undefined|Promise} | |
**/ | |
emit(eventName, ...args) { | |
const {_event_queue, _handle_promise} = _EventEmitter.get(this); | |
const name = eventName.toString(); | |
return (_handle_promise?__EMIT_PROMISE:__EMIT)(this, _event_queue[name], args); | |
} | |
/** | |
* Add a listener to a specific event | |
* | |
* @param {string} eventName The event the listener will listen to | |
* @param {function} listener The listener | |
* @returns {EventEmitter} Return the emitter instance for chaining | |
**/ | |
addListener(eventName, listener) { | |
if ( typeof listener !== "function" ) { | |
throw new TypeError( "Given listener should be a function" ); | |
} | |
const {_event_queue} = _EventEmitter.get(this); | |
const name = eventName.toString(); | |
const queue = _event_queue[name] = _event_queue[name]||[]; | |
queue.push(listener); | |
return this; | |
} | |
/** | |
* Add a listener to a specific event | |
* | |
* @param {string} eventName The event the listener will listen to | |
* @param {function} listener The listener to be added | |
* @returns {EventEmitter} Return the emitter instance for chaining | |
**/ | |
on(eventName, listener) { | |
return this.addListener(eventName, listener); | |
} | |
/** | |
* Remove all the specific event's listeners. | |
* | |
* @param {string} eventName The event to remove | |
* @returns {EventEmitter} | |
**/ | |
removeAllListeners(eventName) { | |
const PRIVATES = _EventEmitter.get(this); | |
const name = eventName.toString(); | |
delete PRIVATES._event_queue[name]; | |
return this; | |
} | |
/** | |
* Remove a listener from a specific event | |
* | |
* @param {string} eventName The event where the listener locates | |
* @param {function} listener The target listener to be removed | |
* @returns {EventEmitter} Return the emitter instance for chaining | |
**/ | |
removeListener(eventName, listener) { | |
const {_event_queue} = _EventEmitter.get(this); | |
const name = eventName.toString(); | |
const queue = _event_queue[name]; | |
if ( queue ) { | |
let index; | |
while( (index = queue.indexOf(listener)) >= 0 ) { | |
queue.splice(index, 1); | |
} | |
} | |
return this; | |
} | |
/** | |
* Remove a listener from a specific event | |
* | |
* @param {string} eventName The event where the listener locates | |
* @param {function} listener The target listener to be removed | |
* @returns {EventEmitter} Return the emitter instance for chaining | |
**/ | |
off(eventName, listener) { | |
if ( arguments.length === 1 ) { | |
return this.removeAllListeners(eventName); | |
} | |
return this.removeListener(eventName, listener); | |
} | |
/** | |
* Add a listener that will be invoked only once to a specific event. | |
* Please note that the listener registered with once cannot be removed by off, removeListener or removeAllListeners. | |
* | |
* @param {string} eventName The event the listener will listen to | |
* @param {function} listener The listener to be added | |
* @returns {EventEmitter} Return the emitter instance for chaining | |
**/ | |
once(eventName, listener) { | |
return this.addListener(eventName, __ONCE_WRAPPER(this, eventName, listener)); | |
} | |
/** | |
* Retrieve a copy of specific event's listener queue | |
* | |
* @param {string} eventName The specific event name | |
* @returns {function[]} The listener queue | |
**/ | |
listeners(eventName) { | |
const {_event_queue} = _EventEmitter.get(this); | |
const name = eventName.toString(); | |
return (_event_queue[name]||[]).slice(0); | |
} | |
/** | |
* Retrieve the listener number of specific event | |
* | |
* @param {string} eventName The specific event name | |
* @returns {number} The amount of listeners | |
**/ | |
listenerCount(eventName) { | |
const {_event_queue} = _EventEmitter.get(this); | |
const name = eventName.toString(); | |
return (_event_queue[name]||[]).length; | |
} | |
/** | |
* Retrieve the registered event names | |
* | |
* @property-read {string[]} events | |
**/ | |
get events() { | |
const {_event_queue} = _EventEmitter.get(this); | |
const _events = []; | |
for( let name in _event_queue ) { | |
if ( !_event_queue.hasOwnProperty(name) ) continue; | |
if ( _event_queue[name].length === 0 ) continue; | |
_events.push(name); | |
} | |
return _events; | |
} | |
set events(val) { throw new TypeError("Cannot assign to read only property 'events' of <EventEmitter>"); } | |
/** | |
* @property {boolean} handlePromise | |
**/ | |
get handlePromise() { | |
const {_handle_promise} = _EventEmitter.get(this); | |
return _handle_promise; | |
} | |
set handlePromise(value) { | |
const PRIVATES = _EventEmitter.get(this); | |
PRIVATES._handle_promise = !!value; | |
} | |
/** | |
* Revert to the previous version if any... | |
* | |
* @returns {EventEmitter} | |
**/ | |
static noConflic() { | |
if ( _previous ) { | |
exports.EventEmitter = _previous; | |
} | |
return exports.EventEmitter; | |
} | |
} | |
exports.EventEmitter = EventEmitter; | |
return exports; | |
function __ONCE_WRAPPER(emitter, eventName, listener) { | |
if ( typeof listener !== "function" ) { | |
throw new TypeError( "Given listener should be a function" ); | |
} | |
const once = function(...args) { | |
const {_event_queue, _handle_promise} = _EventEmitter.get(this); | |
const name = eventName.toString(); | |
const queue = _event_queue[name] = _event_queue[name]||[]; | |
let index = queue.indexOf(once); | |
if ( !_handle_promise ) { | |
listener.call(emitter, ...args); | |
if ( index >= 0 ) { | |
queue.splice(index, 1); | |
} | |
return; | |
} | |
return Promise.resolve(listener.call(emitter, ...args)) | |
.then(()=>{ | |
if ( index >= 0 ) { | |
queue.splice(index, 1); | |
} | |
}); | |
}; | |
return once; | |
} | |
async function __EMIT_PROMISE(emitter, queue, args) { | |
if ( Array.isArray(queue) ) { | |
for( let func of queue ) { | |
await func.call(emitter, ...args); | |
} | |
} | |
} | |
function __EMIT(emitter, queue, args) { | |
if ( Array.isArray(queue) ) { | |
for( let func of queue ) { | |
func.call(emitter, ...args); | |
} | |
} | |
} | |
})(window?window:(module?module.exports:{})); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(a=>{"use strict";function b(a,b,c){if("function"!=typeof c)throw new TypeError("Given listener should be a function");const d=function(...e){const{_event_queue:g,_handle_promise:h}=f.get(this),i=b.toString(),j=g[i]=g[i]||[];let k=j.indexOf(d);return h?Promise.resolve(c.call(a,...e)).then(()=>{0<=k&&j.splice(k,1)}):(c.call(a,...e),void(0<=k&&j.splice(k,1)))};return d}async function c(a,b,c){if(Array.isArray(b))for(let d of b)await d.call(a,...c)}function d(a,b,c){if(Array.isArray(b))for(let d of b)d.call(a,...c)}const e=a.EventEmitter,f=new WeakMap;class g{constructor(){const a={};f.set(this,a),a._event_queue=[],a._handle_promise=!1}emit(a,...b){const{_event_queue:e,_handle_promise:g}=f.get(this),h=a.toString();return(g?c:d)(this,e[h],b)}addListener(a,b){if("function"!=typeof b)throw new TypeError("Given listener should be a function");const{_event_queue:c}=f.get(this),d=a.toString(),e=c[d]=c[d]||[];return e.push(b),this}on(a,b){return this.addListener(a,b)}removeAllListeners(a){const b=f.get(this),c=a.toString();return delete b._event_queue[c],this}removeListener(a,b){const{_event_queue:c}=f.get(this),d=a.toString(),e=c[d];if(e)for(let a;0<=(a=e.indexOf(b));)e.splice(a,1);return this}off(a,b){return 1===arguments.length?this.removeAllListeners(a):this.removeListener(a,b)}once(a,c){return this.addListener(a,b(this,a,c))}listeners(a){const{_event_queue:b}=f.get(this),c=a.toString();return(b[c]||[]).slice(0)}listenerCount(a){const{_event_queue:b}=f.get(this),c=a.toString();return(b[c]||[]).length}get events(){const{_event_queue:a}=f.get(this),b=[];for(let c in a)a.hasOwnProperty(c)&&(0===a[c].length||b.push(c));return b}set events(a){throw new TypeError("Cannot assign to read only property 'events' of <EventEmitter>")}get handlePromise(){const{_handle_promise:a}=f.get(this);return a}set handlePromise(a){const b=f.get(this);b._handle_promise=!!a}static noConflic(){return e&&(a.EventEmitter=e),a.EventEmitter}}return a.EventEmitter=g,a})(window?window:module?module.exports:{}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment