npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@rxstack/async-event-dispatcher

v0.8.0

Published

RxStack AsyncEventDispatcher Component

Downloads

112

Readme

The AsyncEventDispatcher Component

The AsyncEventDispatcher component provides tools that allow your application components to communicate with each other by dispatching events and listening to them.

Installation

  1. npm install @rxstack/async-event-dispatcher --save

  2. You need to install reflect-metadata shim:

    npm install reflect-metadata --save

    and import it somewhere in the global place of your app (for example in app.ts):

    import "reflect-metadata";

Usage

Events

When an event is dispatched, it's identified by a unique name (e.g. order.placed), which any number of listeners might be listening to. An Event instance is also created and passed to all of the listeners. As you'll see later, the Event object itself often contains data about the event being dispatched.

Naming Conventions

The unique event name can be any string, but optionally follows a few simple naming conventions:

  • Use only lowercase letters, numbers, dots (.) and underscores (_);
  • Prefix names with a namespace followed by a dot (e.g. order., user.*);
  • End names with a verb that indicates what action has been taken (e.g. order.placed).

Event Objects

When the dispatcher notifies listeners, it passes an actual GenericEvent object (or one that extends it) to those listeners. The base Event class is very simple: it contains a method for stopping event propagation, but not much else.

The Dispatcher

The dispatcher is the central object of the event dispatcher system. In general, a single dispatcher is created, which maintains a registry of listeners. When an event is dispatched via the dispatcher, it notifies all listeners registered with that event:

import { AsyncEventDispatcher } from '@rxstack/async-event-dispatcher'

const dispatcher = new AsyncEventDispatcher();

Connecting Listeners

To take advantage of an existing event, you need to connect a listener to the dispatcher so that it can be notified when the event is dispatched. A call to the dispatcher's addListener() method associates any valid callable to an event:

// ...

dispatcher.addListener('pre.foo', async (event: CustomEvent): Promise<void> => {
  // do something
}, 10);

The addListener() method takes up to three arguments:

  1. The event name (string) that this listener wants to listen to;
  2. A callable that will be executed when the specified event is dispatched;
  3. An optional priority integer (higher equals more important and therefore that the listener will be triggered earlier) that determines when a listener is triggered versus other listeners (defaults to 0). If two listeners have the same priority, they are executed in the order that they were added to the dispatcher.

Creating an Event Class

Suppose you want to create a new event - order.placed - that is dispatched each time a customer orders a product with your application. When dispatching this event, you'll pass a custom event instance that has access to the placed order. Start by creating this custom event class and documenting it:

export class OrderEvent {
  static readonly eventName = 'order.placed'
  
  constructor(protected order: Order) { }
  
  getOrder(): Order {
    return this.order;
  }
}

Each listener now has access to the order via the getOrder() method.

Dispatch the Event

The dispatch() method notifies all listeners of the given event. It takes two arguments: the name of the event to dispatch and the Event instance to pass to each listener of that event (optional):

// the order is somehow created or retrieved
const order = new Order();

// creates the OrderEvent and dispatches it
const event = new OrderEvent(order);
await dispatcher.dispatch(OrderEvent.eventName, event);

Notice that the special OrderEvent object is created and passed to the dispatch() method and a promise is returned and needs to be resolved. Now, any listener to the order.placed event will receive the OrderEvent.

Stopping Event Flow/Propagation

In some cases, it may make sense for a listener to prevent any other listeners from being called. In other words, the listener needs to be able to tell the dispatcher to stop all propagation of the event to future listeners (i.e. to not notify any more listeners). This can be accomplished from inside a listener via the stopPropagation() method:

// ...

dispatcher.addListener('pre.foo', async (event: CustomEvent): Promise<void> => {
  // do something
  event.stopPropagation();
});

Now, any listeners to pre.foo that have not yet been called will not be called.

Using decorators to register event listeners

Sometimes you may want to use classes to register event listeners. In this case you can take advantage of @Observe decorator.

// ...

export class Observer {
  @Observe('pre.foo', 10)
  async preFoo(event: GenericEvent): Promise<void> {
    // do something
  }
}

Now metadata from @Observe decorator will be added to 'reflect-metadata' storage.

Let's register the event listener:

import {
  AsyncEventDispatcher, EVENT_LISTENER_KEY, EventListenerMetadata,
  ObserverMetadata
} from '@rxstack/async-event-dispatcher';

const observerInstance = new Observer();
const metadata: EventListenerMetadata = Reflect.getMetadata(EVENT_LISTENER_KEY, observerInstance.constructor);
metadata.observers.forEach((observer: ObserverMetadata) => {
    dispatcher.addListener(
      observer.eventName,
      observerInstance[observer.propertyKey].bind(observerInstance),
      observer.priority
    );
});

This approach is useful if your application is using a dependency injector.

License

Licensed under the MIT license.