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

atvik

v3.2.1

Published

Event handling using individual emitters and signals

Downloads

2,812

Readme

Atvik

npm version Build Status Coverage Status Dependencies

Atvik is an event emitter for JavaScript and TypeScript. This library provides emitters for individual events that work well with types and inheritance.

import { Event } from 'atvik';

// Create an event
const event = new Event(thisValueForListeners);

// Subscribe to the event
const handle = event.subscribe((arg1) => console.log('event', arg1));

// Emit the event, triggering all listeners
event.emit('first argument');

// Unsubscribe from the event
handle.unsubscribe();

// Using for await ... of listeners
for await (const [ arg1 ] of event) {
  console.log('event', arg1);
}

// Public API without emit is available
const subscribable = event.subscribable;
subscribable((arg1) => console.log('event', arg1));

for await (const [ arg1 ] of subscribable) {
  console.log('event', arg1);
}

Use with classes

Events come with a public API called Subscribable for use with classes so that users of a class can only subscribe to events and not emit them.

class Counter {
  constructor() {
    this.countUpdatedEvent = new Event(this);
    this.count = 0;
  }

  get onCountUpdated() {
    /*
     * Return the subscribable of the event - which is a function that can be
     * used to listen to the event.
     */
    return this.countUpdatedEvent.subscribable;
  }

  increment() {
    this.count++;
    this.countUpdatedEvent.emit(this.count);
  }
}

const counter = new Counter();

// Subscribe to the event with a listener
counter.onCountUpdated(currentCount => console.log(currentCount));

// Increment and trigger the countUpdated event
counter.increment();

Subscribable is a function that can be used to directly subscribe a listener, but can also be used for more advanced use cases. The following functions are supported:

  • subscribe(listener: Listener): SubscriptionHandle - Subscribe a listener, the same as invoking the function directly
  • unsubscribe(listener: Listener): void - Unsubscribe a listener
  • once(): Promise - Create a promise that will resolve once the event is emitted
  • filter(filter: (...args) => boolean) - Filter the subscribable, returning an up
  • withThis(newThis) - Change the this used for listeners

Types with TypeScript

Atvik is compatible with TypeScript and provides a type-safe interface to listen to and emit events:

import { Event } from 'atvik';

const parent = {};

// Create an event without any expected arguments
const noArgEvent = new Event<object>(parent);

// Subscribing will be checked so it takes in zero arguments
noArgEvent.subscribe(() => /* do stuff here */);

// Emitting the event can only be done without any parameters
noArgEvent.emit();

Events can have arguments that will be checked in the listeners and when emitting:

// Pass a second type in array form to specify the expected arguments
const argEvent = new Event<object, [ number ]>(parent);

// Subscribe will now check that the arguments are compatible
argEvent.subscribe((count) => /* do stuff here */);

// Emitting the event now requires arguments to be passed
argEvent.emit(10);

Listening to something once

Listening for a single event can be done via promises:

// Wait for the event to be emitted
const args = await event.once();

// Or using the subscribable
const args = await event.subscribable.once();

Filtering events

In some cases it might be useful to filter events without managing a separate Event. Atvik supports creating a filtered Subscribable for this purpose:

const event = new Event(thisValueForListener);

const onlyEvenNumbers = event.filter((arg1) => arg1 % 2 === 0);
onlyEvenNumbers(number => console.log('Got number:', number));

// Will not invoke listener added via onlyEvenNumbers
event.emit(1);

// This will invoke the listener
event.emit(2);

Iterating over events

Events and subscribables can be iterated over using a for await .. of loop, allowing for the creation of simple event loops:

for await (const [ arg1 ] of event) {
  console.log('event', arg1)
}

Sometimes events are emitted faster than they can be consumed, limiting and controlling overflow of events can be done via {@link iterator}.

As an example this will limit to 10 queued events and then start dropping the earliest ones:

for await (const [ arg1 ] of subscribable.iterator({ limit: 10 })) {
  console.log('event', arg1);
}

The behavior to use when the queue is full can be controlled by setting the OverflowBehavior:

const iteratorOptions = {
  limit: 10,
  overflowBehavior: OverflowBehavior.DropNewest
};

for await (const [ arg1 ] of subscribable.iterator(iteratorOptions)) {
  console.log('event', arg1);
}

Monitoring for listener changes

For some use cases it is necessary to monitor if an event has any listeners, for this library provides the monitorListeners function. If a monitor is registered it will be invoked for any change in listeners, so subscribing or unsubscribing will always trigger the monitor.

Example with a fictional service being started and stopped:

event.monitorListeners(theEvent => {
  if(theEvent.hasListeners) {
    // The event has at least one active listener
    if(! service.started) {
      service.start();
    }
  } else {
    // No active listeners
    if(service.started) {
      service.stop();
    }
  }
});

Only a single monitor may be active at a time and the active monitor can be removed via removeMonitor().

Asynchronous subscription and unsubscription

AsyncSubscribable is a variant of Subscribablewhere listeners are subscribed in an asynchronous way. It is intended for use when listeners need some asynchronous action before they are available, such as a remote RPC scenario. The API of AsyncSubscribable matches Subscribable but returns promises for subscribe, unsubscribe and emit:

// Subscribe to the event
const handle = await asyncSubscribable.subscribe((arg1) => /* do stuff here */);

// Unsubscribe from the event
await handle.unsubscribe();

An implementation can be created via createAsyncSubscribable to create a bridge to something like a remote service, or via AsyncEvent for local use.

Using createAsyncSubscribable:

import { createAsyncSubscribable } from 'atvik';

const asyncSubscribable = createAsyncSubscribable({
  subscribe: async (listener) => {
    // Subscribe listener here
  },
  unsubscribe: async (listener) => {
    // Unsubscribe listener here
    return listenerWasSubscribed;
  }
});

Using AsyncEvent:

import { AsyncEvent } from 'atvik';

const event = new AsyncEvent(thisValueForListeners);

// Emit the event, triggering all listeners
await event.emit('first argument');

Adapting other event emitters

Support is included for adapting some common event emitters such as Nodes EventEmitter and DOM events using createEventAdapter.

import { createEventAdapter } from 'atvik';

const subscribable = createEventAdapter(eventEmitter, 'nameOfEvent');

// Use subscribable as normal
subscribable(arg1 => console.log('event', arg1));