@stoutmusclenugget/create-emitter
TypeScript icon, indicating that this package has built-in type declarations

0.2.0 • Public • Published

createEmitter()

This package provides a flexible API for creating an event emitter that forces events to execute and resolve in the order in which they are called. This emitter supports both synchronous and asynchronous event handling, ensuring that events are processed sequentially. Additionally, subscriptions are supported and can be triggered from these events.

Table of Contents

  1. Installation
  2. Why?
  3. Usage
  1. API
  1. Types
  1. License

Installation

npm install @stoutmusclenugget/create-emitter
yarn add @stoutmusclenugget/create-emitter
pnpm add @stoutmusclenugget/create-emitter
import { createEmitter, type CreateEmitter } from '@stoutmusclenugget/create-emitter';

Why?

Usage

Creating an Emitter

Call the createEmitter() function by passing in a configuration object - a configuration object can map any value to its keys, including: functions, objects, primitives, etc.

import { createEmitter } from '@stoutmusclenugget/create-emitter';

const emitter = createEmitter({
  async asynchronousMethod() {},
  synchronousMethod() {},
  staticNumber: 1,
  staticString: 'example',
  staticObject: {},
});

You then have access to all of the methods and properties passed from the configuration object. Of note, the signature of each method and property remains unchanged. Methods are wrapped in a special queueing functionality that allows us to fire events off in sequential order to avoid race conditions.

await emitter.asynchronousMethod(); // Promise<void>
emitter.synchronousMethod(); // void
emitter.staticNumber; // 1
emitter.staticString; // 'example'
emitter.staticObject; // {}

In addition to the methods and properties you passed in, you now have access to the following methods and properties, which I will explain in depth further down:

  1. const unsubscribe = emitter.subscribe(): Allows you to subscribe to any method you passed into the initial configuration object.
  2. emitter.enable();: Enables subscriptions to be triggered when their associated methods are called.
  3. emitter.disable();: Disables subscriptions to method calls.
  4. emitter.enabled;: A boolean value that tells you if subscriptions are enabled/disabled.
  5. emitter.flushing;: A boolean value that tells you if the event queue is being flushed.
  6. emitter.initialized;: A boolean value that tells you if the emitter has been intitialized.

API

function createEmitter<T extends Config>(config: T): Emitter<T>

This function is used to create an emitter. You pass in a configuration object that maps keys to any other value. If any number of functions are passed in, under the hood we wrap these values in special logic that forces the functions to be executed in the order they were called. Furthermore, you can subscribe to any of these functions to trigger side-effects of your own.

Things to Know

  1. The config parameter is an object that can contain methods, properties, and an optional initialize method.
  2. If provided, the initialize function when triggered will be executed once before the first interaction with the emitter.
  3. Returns an Emitter object that includes the same methods and properties as those defined in the config object. Additionally, it includes methods for subscribing to events and managing the emitter's internal state.
  4. All methods are processed sequentially to avoid race conditions by maintaining an internal queue to manage the order of event execution.
import { createEmitter } from '@stoutmusclenugget/create-emitter';

const emitter = createEmitter({
  async asynchronousMethod() {},
  synchronousMethod() {},
  staticNumber: 1,
  staticString: 'example',
  staticObject: {},
});

await emitter.asynchronousMethod(); // Promise<void>
emitter.synchronousMethod(); // void
emitter.staticNumber; // 1
emitter.staticString; // 'example'
emitter.staticObject; // {}

emitter.subscribe(subscription: Subscription<T>): () => void

To trigger side-effects when using the createEmitter function, you can use the subscribe method provided by the emitter. This method allows you to define functions that will be called whenever the corresponding methods in the emitter are triggered. You can also use the all and catch handlers to handle all events and errors, respectively.

Things to Know

  1. When you call emitter.subscribe(), an unsubcribe function is returned.
  2. Anything you return from a config method will get passed directly to each subscription as its first argument.
  3. Any arguments to a config method will get passed directly to each subscription as its subsequent arguments.
  4. If you call the unsubscribe() function that is returned, the subscription will be removed and no longer get triggered.
  5. You can subscribe to all events using a single handler with the key all.
  6. You can subscribe to any errors that get thrown within methods using the key catch.
import { createEmitter } from '@stoutmusclenugget/create-emitter';

// Whatever you return from a method then gets passed to each subscription as the first argument.
const emitter = createEmitter({
  async asynchronousMethod() {
    return 'asynchronousMethod';
  },
  synchronousMethod() {
    return 'synchronousMethod';
  },
});

// When you subscribe, you receive an unsubscribe() function as a return value.
const unsubscribe = emitter.subscribe({
  asynchronousMethod(method) {
    console.log(`Executing `${method}` subscription...`);
  },
  synchronousMethod(method) {
    console.log(`Executing `${method}` subscription...`);
  },
});

emitter.asynchronousMethod(); // log: 'Executing asynchronousMethod subscription...'
emitter.synchronousMethod(); // log: 'Executing synchronousMethod subscription...'

unsubscribe(); // Unsubscribing removes the subscription so it is no longer executed.

emitter.asynchronousMethod(); // Nothing logs.
emitter.synchronousMethod(); // Nothing logs.

emitter.enable(): void

The emitter can be enabled using the enable() method. When enabled, subscriptions are triggered. Subscriptions are enabled by default.

const emitter = createEmitter({
  method() {},
});

emitter.subscribe({
  method() {
    console.log(`Executing method subscription...`);
  },
});

emitter.disable();

emitter.method(); // Logs Nothing.

emitter.enable();

emitter.method(); // log: 'Executing method subscription...'

emitter.disable(): void

The emitter can be disabled using the disable() method. When disabled, subscriptions are not triggered.

const emitter = createEmitter({
  method() {},
});

emitter.subscribe({
  method() {
    console.log(`Executing method subscription...`);
  },
});

emitter.method(); // log: 'Executing method subscription...'

emitter.disable();

emitter.method(); // Logs nothing.

emitter.enabled: boolean

emitter.flushing: boolean

emitter.initialized: boolean

Types

Fn

type Fn = (...args: any) => any;

Config

type Config = Record<string, any> & { initialize?: Fn };

Subscription

type Subscription<C extends ConditionalPick<Config, Fn>, Key extends keyof C = keyof C> = Readonly<
  {
    [Key in keyof C]?: (payload: AsyncReturnType<C[Key]>, ...args: Parameters<C[Key]>) => unknown;
  } & {
    all?<K extends Key>(key: K, payload: AsyncReturnType<C[K]>): unknown;
    all?<K extends Key>(key: K, payload: AsyncReturnType<C[K]>, ...args: Parameters<C[K]>): unknown;
    catch?<K extends Key>(key: K, payload: Error): unknown;
    catch?<K extends Key>(key: K, payload: Error, ...args: Parameters<C[K]>): unknown;
  }
>;

Emitter

type Emitter<C extends Config> = C & {
  __SUBSCRIPTIONS__: Map<symbol, Subscription<C>>;
  get enabled(): boolean;
  get flushing(): boolean;
  get initialized(): boolean;
  disable(): void;
  enable(): void;
  subscribe(subscription: Subscription<ConditionalPick<C, Fn>>): () => void;
};

License

This project is licensed under the MIT License.

Readme

Keywords

none

Package Sidebar

Install

npm i @stoutmusclenugget/create-emitter

Weekly Downloads

0

Version

0.2.0

License

none

Unpacked Size

16.1 kB

Total Files

7

Last publish

Collaborators

  • stoutmusclenugget