kilo-emitter
TypeScript icon, indicating that this package has built-in type declarations

0.1.8 • Public • Published

What & Why

A sub 1Kb event emitter class with broader functionality.

Why another one ?

I needed some specific but not unheard of features of event emitter and all the lightweight implementations I founds were all missing one or the other, so I ended up making my own with set of features that I needed. It's not better or worse than other ones, it just combines a specific set.

So here's what I needed from event emitter and ended up implementing in this one:

  • Small footprint, 1Kb or less.
  • Chainalbe methods.
  • Instantiate with new as well as extend existing object.
  • Reliable listener removal (usually once were implemented as wrappers so you couldn't remove them by same listener reference).
  • Ability to remove all listeners for given event. See .off(event)
  • Ability to remove all listeners for all events. See .off(event)
  • Priority listeners (added to the start of the queue instead of end). See .on()
  • Ability to pass context to the listener as this keyword. See .on()
  • Ability to stop propagation of current event, smth like preventDefaults. See .emit()
  • Ability to pass arguments together with event. See .emit()
  • Ability to check whether specific listener is subscribed for a specific event. See .triggers()
  • Ability to check whether emitter has any listeners subscribed for specified event or any listeners at all. See .triggers()
  • Available under diferent formats (ES3, ES5, ES6, Browser, inlined)

Stats

Coverage

Type Coverage
Statements 100% (130/130)
Branches 100% (84/84)
Functions 100% (18/18)
Lines 100% (126/126)

dist directory size listing

Name Bytes Gzip %
Emitter.es3.browser.js 3289 813 24%
Emitter.es3.browser.min.js 1022 501 49%
Emitter.es3.inlined.js 3286 810 24%
Emitter.es3.inlined.min.js 1019 499 48%
Emitter.es3.umd.js 4120 976 23%
Emitter.es3.umd.min.js 1276 607 47%
Emitter.es6.inlined.js 3069 789 25%
Emitter.es6.inlined.min.js 883 481 54%
Emitter.es6.js 3076 794 25%
Emitter.es6.min.js 899 493 54%
Emitter.js 3161 843 26%
Emitter.min.js 975 535 54%

Usage

Package manager
npm i kilo-emitter
yarn add kilo-emitter
Browser tag
<script src="https://unpkg.com/kilo-emitter/dist/Emitter.es3.browser.min.js"></script>
Inlined

Or you can just grab this compiled inlined version and copy-paste it in your code.

ES3, 1019 Bytes

var Emitter=function(){function t(){this.$evt={}}return t.extend=function(e){var n;return e&&"object"==typeof e&&(n=new t,["$evt","on","off","once","emit","triggers"].forEach(function(t){e[t]=n[t]})),e},t.prototype.on=function(t,e,n,o){var i,r=this.$evt;return t&&e&&("boolean"==typeof n?(o=n,n=null):e.$ctx=n,(i=r[t])?(this.off(t,e),o?i.unshift(e):i.push(e)):i=[e],r[t]=i),this},t.prototype.once=function(t,e,n,o){return t&&e&&(e.$once=!0,this.on(t,e,n,o)),this},t.prototype.off=function(t,e){var n,o,i=arguments.length,r=this.$evt;return 0===i?this.$evt={}:1===i?delete r[t]:-1<(n=(o=r[t])?o.indexOf(e):-1)&&(o.splice(n,1),o.length||delete r[t]),this},t.prototype.emit=function(t,e){var n,o,i,r=this.$evt[t];if(r&&(i=r.length))for(r=r.slice(),n=0;n<i;n++)"stopEmit"===(o=r[n]).apply(o.$ctx,e)&&(n=i),o.$once&&(this.off(t,o),delete o.$once);return this},t.prototype.triggers=function(t,e){var n,o=arguments.length,i=this.$evt;return o?!!(n=i[t])&&(!(1<o)||-1<n.indexOf(e)):!!Object.getOwnPropertyNames(i).length},t}();

ES6, 883 Bytes

class Emitter{static extend(t){let e;return t&&"object"==typeof t&&(e=new Emitter,["$evt","on","off","once","emit","triggers"].forEach(n=>{t[n]=e[n]})),t}constructor(){this.$evt={}}on(t,e,n,i){let s,r=this.$evt;return t&&e&&("boolean"==typeof n?(i=n,n=null):e.$ctx=n,(s=r[t])?(this.off(t,e),i?s.unshift(e):s.push(e)):s=[e],r[t]=s),this}once(t,e,n,i){return t&&e&&(e.$once=!0,this.on(t,e,n,i)),this}off(t,e){let n,i,s=arguments.length,r=this.$evt;return 0===s?this.$evt={}:1===s?delete r[t]:(n=(i=r[t])?i.indexOf(e):-1)>-1&&(i.splice(n,1),i.length||delete r[t]),this}emit(t,e){let n,i,s,r=this.$evt[t];if(r&&(s=r.length))for(r=r.slice(),n=0;n<s;n++)"stopEmit"===(i=r[n]).apply(i.$ctx,e)&&(n=s),i.$once&&(this.off(t,i),delete i.$once);return this}triggers(t,e){let n,i=arguments.length,s=this.$evt;return i?!!(n=s[t])&&(!(i>1)||n.indexOf(e)>-1):!!Object.getOwnPropertyNames(s).length}}
Node
const Emitter = require('kilo-emitter')
let cb = (World) => { console.log(`Hello ${World}!`) }
 
let myEmitter = new Emitter()
myEmitter.on('evt', cb)
myEmitter.emit('evt', ['World']) // `Hello World!`
console.log(myEmitter.triggers('evt', cb)) // true
myEmitter.off('evt')
console.log(myEmitter.triggers('evt', cb)) // false
ES6/TypeScript
import Emitter from 'kilo-emitter'
 
let myEmitter = new Emitter()
myEmitter.once('evt', console.log)
myEmitter.once('evt2', console.log)
myEmitter.emit('evt', ['hey there']) // 'hey there'
console.log(myEmitter.triggers('evt', console.log)) // false
console.log(myEmitter.triggers()) // true
myEmitter.off()
console.log(myEmitter.triggers()) // false
Browser
<script src="https://unpkg.com/kilo-emitter/dist/Emitter.es3.browser.min.js"></script>
 
<script>
var myEmitter = new Emitter()
myEmitter.once('evt', (param) => {
     console.log(`Hey ${param}!`)
    }, ['there'])
myEmitter.emit('evt')
</script> 

API

static extend(target)

Extends target object that is passed to it with Emitter class methods. It creates new Emitter class and assigns all of it's fields and methods (including $evt) to target object. Note that those methods will override existing fields with same names and also should now be invoked on target since they rely on this keyword.

function extend<T extends object>(target: T): T & Emitter
  • target object An object that will be extended.
let someObject = {someField: 'someValue'}
Emitter.extend(someObject)
 
someObject.on('evt', function (someParam) {
  console.log(someParam) // 'otherValue'
  console.log(this.someField) // 'someValue'
})
someObject.emit('evt', ['otherValue'])

on(event, listener, context, priority)

Subscribes a listener to an event. Listener will persist until removed with .off(). Subscribing an existing listener again will move it to the end (or start, if priority specified) of the queue. Unique listener is considered a combo of an event name and a reference to a function. If a same callback added with a different context it will be considered as a same listener. Context parameter is skipable, if you pass boolean as 3rd argument it will be used as priority.

function on(event: string, listener: Listener, context?: object|boolean, priority?:Boolean) : this
  • event string Event name you want to subscribe to.
  • listener Listener Listener callback to be invoked.
  • context object|boolean optional Context to invoke callback with (pass as this) OR a boolean value for priority if you want to skip context
  • priority Boolean optional If true will add listener to the start of the queue.
let em = new Emitter()
 
em.on('evt', (val) => {}) // regular listener
em.on('evt', (val) => {}, {someField: 'someValue'}) // listener with context
em.on('evt', (val) => {}, null, true) // listener with priority (added to the start of queue)
em.on('evt', (val) => {}, true) // same
 
em.emit('evt', ['otherValue'])

once(event, listener, context, priority)

Same as .on() but listener will be automatically removed after first invocation.

function once(event: string, listener: Listener, context?: object, priority?:Boolean) : this
  • event string Event name you want to subscribe to.
  • listener Listener Listener callback to be invoked.
  • context object|boolean optional Context to invoke callback with (pass as this) OR a boolean value for priority if you want to skip context
  • priority Boolean optional If true will add listener to the start of the queue.
let em = new Emitter()
let cb = () => {}
 
em.once('evt', cb)
console.log( em.triggers('evt', cb) ) // true
 
em.emit('evt')
console.log( em.triggers('evt', cb) ) // false

off(event, listener)

If both arguments are passed it removes the listener if such exists. If only event name is passed it will remove all the listeners for that event. If no arguments passed it will purge all the listeners for current emitter. Distinction is made by the length of the arguments variable to avoid undesired behaviour when null or undefined are passed due to an error. This means that off('init') will try to remove all listeners for 'init' event and off(null) will try to remove all events for 'null' event.

function off(event?: string, listener?: Function) : this
  • event string optional Event name you want to unsubscribe from.
  • listener Listener optional Listener callback you want to remove.
let em = new Emitter()
let cb = () => {}
em.on('evt', cb) // removed on 1sk off
em.on('evt', () => {}) // removed on 2sk off
em.on('evt2', () => {}) // removed on 3sk off
// removes specific listener
em.off('evt', cb)
// removes all listeners for event
em.off('evt')
// removes all listeners completely
em.off()

emit(event, args)

If both arguments are passed it removes the listener if such exists. If only event name is passed it will remove all the listeners for that event. If no arguments passed it will purge all the listeners for current emitter. Distinction is made by the length of the arguments variable to avoid undesired behaviour when null or undefined are passed due to an error. This means that off('init') will try to remove all listeners for 'init' event and off(null) will try to remove all events for 'null' event.

function emit(event: string, args?:any[]) : this
  • event string Event name whose listeners should be invoked.
  • args any[] optional Array of arguments that should be passed to each listener callback.
let em = new Emitter()
 
// this listeners stops propagation and removes itself
em.once('evt', (arg1, arg2) => {
  console.log(arg1) // 'a'
  console.log(arg2) // 'b'
  return 'stopEmit'
})
 
// this listener is triggered on second emit
em.on('evt', () => {
  // never triggered
})
 
em.emit('evt', ['a', 'b'])
em.emit('evt')

triggers(event, listener)

If both arguments are passed then it will check whether specific listener is subscribed for specific event. If only event name is passed it will check if there are any listeners subscribed for that event. If no arguments passed it will check if emitter has any listeners at all. Distinction is made by the length of the arguments variable to avoid undesired behaviour when null or undefined are passed due to an error. This means that triggers('init') will check if there are any listeners fot the event 'init' and triggers(null) will check if there are any listeners fot the event 'null'.

function triggers(event?: string, listener?: Function): boolean
  • event string optional Event name for which to look.
  • listener Function optional Reference to a function instance that was used when subscribing.
let em = new Emitter()
let cb = () => {}
 
em.on('evt', cb)
em.on('evt', () => {})
em.on('evt3', () => {})
 
console.log( em.triggers('evt', cb) ) // true
em.off('evt', cb)
console.log( em.triggers('evt', cb) ) // false
 
console.log( em.triggers('evt') ) // true
em.off('evt')
console.log( em.triggers('evt') ) // false
 
console.log( em.triggers() ) // true
em.off()
console.log( em.triggers() ) // false

Package Sidebar

Install

npm i kilo-emitter

Weekly Downloads

0

Version

0.1.8

License

MIT

Unpacked Size

82.7 kB

Total Files

21

Last publish

Collaborators

  • n1kk