npm package: @sprs/emitter
Description: Simple event emitter
Author: Brett Mitchell
Latest version: 0.0.2


# `Emitter`

This package provides a simple, slightly opinionated event emitter with a reduced inferface resembling Node.js's `EventEmitter`.

Listener registration is idempotent. Multiple registrations of the same function will not result in multiple invocations of that function.

Use `.on`/`.off`/`.emit` to register/remove listeners and emit events.

Use `.xform` to produce a `XForm` instance for a single event (see the next section for details on `XForm`).

Use the optional `events` parameter to `Emitter`s constructor to specify known events. The emitter is still allowed to emit any event (no error will be thrown if you emit an event which is not declared in `events`). Specifying `events` pre-allocates containers for listeners, attaches known property names to an `events` property on the `Emitter` instance, and provides type annotations for known event names in instance methods.

The `emitter` factory wraps the `Emitter` class's constructor, and provides a way of creating an `Emitter` which does not require the `new` keyword.

## Example 1: The basics

```js

import { Emitter } from '@sprs/emitter';

const e = new Emitter(['a', 'b', 'c']);

const logIt = value => console.log('a emitted ', value);
e.on('a', logIt);

e.emit('a', 123);
// -> Message logged: 'a emitted 123'

e.off('a', logIt);
e.emit('a', 456);
// -> No message logged

```

## Example 2: Emitter subclass

```js

import { Emitter } from '@sprs/emitter';

class RequestEmitter extends Emitter {
  constructor(url, options) {
    super(['init', 'response', 'error'])
    this.url = url;
    this.options = options;
  }

  init() {
    this.emit('init', {
      url: this.url,
      options: this.options,
    });

    fetch(this.url, this.options)
      .then(response => {
        this.emit('response', response);
      })
      .catch(error => {
        this.emit('error', error);
      });
  }
}

const google = new RequestEmitter('https://www.google.com', { method: 'GET' });

google.on('init', (startValues) => console.log('Request started: ', startValues));
google.on('response', (response) => console.log('Request succeeded: ', response));
google.on('error', (error) => console.log('Request failed: ', error));

google.init();

```

## Example 3: Transforming events

Following is a more complicated example using the fluent `xform` API to transform event payloads asynchronously. Most use cases shouldn't require this; if you can, just stick to plain old `Emitter`s.

```js

import { Emitter } from '@sprs/emitter';

class RequestEmitter extends Emitter {
  ...
}

const ddg = new RequestEmitter('https://www.duckduckgo.com', { method: 'GET' });
ddg
  .xform({ response: data => data.text() })
  .xform({ response: text => text.slice(0, 100) })
  .on('response', sliced => console.log('First 100 characters of the Duck Duck Go home page HTML...', sliced))

ddg.init();

```

# `XForm`

The `XForm` class serves as an event transformer. It is constructed from an existing `Emitter` and a set of functions to transform event payloads with, and it has the same `on`/`off` methods as an `Emitter`, but it has no `emit` method.

`XForm` can be used to transform both `Emitter`s from this package, and `EventTarget`s from the standard browser API.

If a transformer function returns a `Promise`, `XForm` will wait for that `Promise` to resolve, and then will emit the resolved value, rather than immediately emitting the `Promise` itself.

The third (optional) parameter to the `XForm` constructor provides options. The `multiReturn` option requires a transformer function to return an array of values. This allows an `XForm` to transform event payloads from `Emitter`s which emit multiple values to their events.

The `xform` factory function wraps the `XForm` class's constructor, and provides a method of creating an `XForm` that does not require the `new` keyword.

```js

import { Emitter, XForm } from '@sprs/emitter';

const e1 = new Emitter();
const x1 = new XForm(e1, { myEvent: payload => payload.myProperty * 5 });
x1.on('myEvent', console.log);
e1.emit('myEvent', { myProperty: 6 });
// -> '30'

const e2 = new EventTarget();
const x2 = new XForm(e2, { myEvent: event => event.detail.myProperty * 5 });
x2.on('myEvent', console.log);
e2.dispatchEvent(new CustomEvent('myEvent', { detail: { myProperty: 6 } }));
// -> '30'

const emitsMultiple = new Emitter();
const x3 = new XForm(
  emitsMultiple,
  { myEvent: (a, b, c) => [a, b * 2, c * 3] },
  { multiReturn: true },
);
x3.on('myEvent', console.log);
emitsMultiple.emit('myEvent', 2, 4, 6);
// -> 2, 8, 18
//    Note that this is NOT [2, 8, 18]

```