Skip to content

silvermine/event-emitter

Repository files navigation

Silvermine Event Emitter

Build Status Coverage Status Dependency Status Dev Dependency Status

What is it?

A small library that provides an event emitter mixin that can be added to your own JS classes / objects to allow them to implement a pubsub/listener/observer pattern by means of events.

There are a number of other libraries out there to do this, but many of them are either not well-tested or have some other deficiency. We needed a solid, quality event emitter mixin that we could use in a variety of projects.

The event emitter mixin is a CommonJS module, and will work both in node.js and in the browser. For server-side usage, simply use the built-in require function to require ('@silvermine/event-emitter') and use the mixin. For browser environments, use a CommonJS module loader like Browserify or Webpack.

@silvermine/event-emitter uses native Promises to make event listener executions asynchronous. Native Promises are available in node.js versions 0.12.18 and up, and in most browsers except Internet Explorer. If you need to support an environment that does not have native Promise support, you can easily add a Promise polyfill.

@silvermine/event-emitter also uses these built-in Array methods that are only available in Internet Explorer 9 and up:

How do I use it?

Creating an event emitter

The object returned by require('@silvermine/event-emitter') is designed to be used as a mixin for javascript "classes". Here are a few different ways in which the mixin can be used:

const { EventEmitterMixin } = require('@silvermine/event-emitter').EventEmitterMixin,
      MyClass, myInstance;

MyClass = function() {};

// Copy functions from EventEmitterMixin to MyClass.prototype
Object.assign(MyClass.prototype, EventEmitterMixin);
// Or use underscore or lodash
_.extend(MyClass.prototype, EventEmitterMixin);

myInstance = new MyClass();
myInstance.emit('hello');

Alternatively, if you prefer to use objects directly, without classes:

var EventEmitterMixin = require('@silvermine/event-emitter').EventEmitterMixin,
    myInstance;

myInstance = Object.create(EventEmitterMixin, {
   doSomething: function() {}
});

myInstance.emit('hello');

If you are using ES6, see this guide on using mixins with ES6 classes. Here is an example of a mixin function that can be used to add event emitter methods to an ES6 class:

const { EventEmitterMixin } = require('@silvermine/event-emitter');

let MixinEventEmitter = (Base) => {
   Object.assign(Base.prototype, EventEmitterMixin);
   return Base;
};

class MyBaseClass {}

class MyClass extends MixinEventEmitter(MyBaseClass) {
   doSomething() {}
}

let myInstance = new MyClass();
myInstance.emit('started');

Basic Usage

Adding and removing event listeners

Once you have an object that has the event emitter functions mixed in, you can begin adding event listeners and emitting events. Use the on function to add an event listener, then use the emit function to emit an event:

myInstance.on('started', function() {
   console.log('myInstance started');
});

// Prints "myInstance started" to the console
myInstance.emit('started');

If you want an event listener to execute only once for a given event, use the once function when adding the listener:

myInstance.once('started', function() {
   console.log('myInstance started');
});

// Prints "myInstance started" to the console
myInstance.emit('started');
// Does nothing
myInstance.emit('started');

If you no longer want to listen to an event, use the off function to remove an event listener:

myInstance.on('started', function() {
   console.log('myInstance started');
});

// Prints "myInstance started" to the console
myInstance.emit('started');

// Remove the event listener for the 'started' event
myInstance.off('started');

// Does nothing
myInstance.emit('started');

You can also remove all event listeners from an object by calling off with no arguments:

myInstance.on('started', function() {
   console.log('myInstance started');
});

myInstance.on('ended', function() {
   console.log('myInstance ended');
});

// Remove all event listeners
myInstance.off();

// Does nothing
myInstance.emit('started');
myInstance.emit('ended');

Chaining

All functions in event emitter are chainable; they return this so that subsequent function calls can chain off of each other:

function listener1() {}
function listener2() {}

myInstance.on('started', listener1)
   .on('stopped', listener2)
   .off('paused')
   .emit('started')

Passing arguments with emitted events

You may want to pass arguments to listener functions when emitting an event. Any arguments given to emit after the event name(s) argument will be passed to any listener functions:

function onStarted(instance, initialValue) {
   console.log(instance, 'was started with an initialValue of', initialValue);
}

myInstance.on('started', onStarted);

myInstance.emit('started', myInstance, 42);

Advanced usage

Binding a listener function to multiple events

You may want to bind one event listener to several event names. You can either bind them individually or within a single call to on by passing an array of event names:

function onChange {}

myInstance.on([ 'started', 'stopped', 'paused' ], onChange);

// or:
myInstance.on('started', onChange)
   .on('stopped', onChange)
   .on('paused', onChange);

Emitting multiple events

You can emit multiple events with a single call to emit by passing an array of event names:

myInstance.emit([ 'initialized', 'started', 'played' ]);

Removing a single listener when multiple listeners are bound to the same event name

If you add multiple event listeners to the same object for the same event name, you may remove a single event listener without affecting the others by passing in the listener function to the off function:

function listener1 {}
function listener2 {}
function listener3 {}

myInstance.on('started', listener1);
myInstance.on('started', listener2);
myInstance.on('started', listener3);

myInstance.off('started', listener2);

// listener1 and listener3 are executed, but listener2 is not
myInstance.emit('started');

Using a custom this context for a listener function

Sometimes you need a function to execute with a certain this context. It may be tempting to bind the context directly to the listener function:

myInstance.on('started', app.onStarted.bind(app));

However, using .bind(app) makes it impossible to remove that specific listener when there are multiple listeners bound to the same event name:

function listener1 {}
function listener2 {}

myInstance.on('started', listener1.bind(this));
myInstance.on('started', listener2.bind(this));

// Does nothing. listener2 remains bound.
myInstance.off('started', listener2);
Why does this happen?

Binding a this context to a function creates a new instance of Function, so when we search for the listener function to remove it, the identity check fails:

function listener() {}

listener.bind(this) === listener.bind(this); // false
So how do I bind a this context to a listener?

To use a custom this context when binding a listener function, pass the context in the call to on instead:

function listener1 {}
function listener2 {}

myInstance.on('started', listener1, this);
myInstance.on('started', listener2, this);

To remove a specific listener and context combination, pass the listener and context to off:

// Removes listener2, but listener1 remains
myInstance.off('started', listener2, this);

// or:
myInstance.off('started'); // removes all listeners bound to 'started'
A warning about arrow functions

JavaScript does not allow you to re-bind the this context of an arrow function. If you pass an arrow function as a listener, the context parameter will have no effect. For example:

const outerContext = this;

function registerListeners() {
   // The `outerContext` parameter will be ignored. The listener will have the `this`
   // context that `registerListeners` is called with.
   myInstance.on('started', () => { console.log('Started', this.name); }, outerContext);
}

If you need to re-bind the context, use a function statement instead:

const outerContext = this;

function registerListeners() {
   // The listener function will have `outerContext` as its `this` context.
   myInstance.on('started', function() { console.log('Started', this.name); }, outerContext);
}

How do I contribute?

We genuinely appreciate external contributions. See our extensive documentation on how to contribute.

License

This software is released under the MIT license. See the license file for more details.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published