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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@dldc/pubsub

v7.0.1

Published

A simple pub/sub written in Typescript

Readme

📫 PubSub

A simple pub/sub written in Typescript

npm install @dldc/pubsub

Gist

import { createSubscription } from '@dldc/pubsub';

const mySub = createSubscription<number>();

const unsub = mySub.subscribe((num) => {
  console.log('num: ' + num);
});

mySub.emit(45); // num: 45

unsub();

Guide

Creating a Subscription

To create a Subscription you need to import the createSubscription function and call it.

import { createSubscription } from '@dldc/pubsub';

const subscription = createSubscription();

If you use TypeScript, you need to pass a type parameter to the createSubscription function to define the type of the value associated with the subscription.

import { createVoidSubscription } from '@dldc/pubsub';

const numSubscription = createSubscription<number>();

If you don't want your subscription to emit any value, you can use the createVoidSubscription function.

import { createVoidSubscription } from '@dldc/pubsub';

const voidSubscription = createVoidSubscription();

Subscribe and Unsubscribe

You have two ways to subscribe / unsubscribe.

  • Using the reference of the callback function
const callback = () => {
  /*...*/
};

subscription.subscribe(callback);
// later
subscription.unsubscribe(callback);
  • Using a SubId (a string)
subscription.subscribeById('mySubId', () => {
  /*...*/
});
// later
subscription.unsubscribeById('mySubId');

In both case the subscribe[ById] return a function that will unsubscribe:

const unsub = subscription.subscribe(/*...*/);
// later
unsub();

Emitting value

To emit a value and trigger all subscribed callback you need to call the emit method.

subscription.emit(42);
// for void subscription you don't need to pass any value
voidSubscription.emit();

OnUnsubscribe

The subscribe[ById] methods accept a optional function after the callback, this function will be called when this callback you are subscribing is unsubscribed.

subscription.subscribe(
  () => {
    /* ... */
  },
  () => {
    console.log('Unsubscribed !');
  },
);

// or with a subId
subscription.subscribeById(
  'mySub',
  () => {
    /* ... */
  },
  () => {
    console.log('Unsubscribed !');
  },
);

Unsubscribing all subscriptions

You can call unsubscribeAll method on a subscription to remove all callback. This will also trigger the onUnsubscribe if any.

subscription.unsubscribeAll();

Subscription options

The createSubscription (or createVoidSubscription) functions accept an option object as parameter (all properties are optional):

const sub = Subscription.create({
  onFirstSubscription: () => {},
  onLastUnsubscribe: () => {},
  onDestroy: () => {},
  maxSubscriptionCount: 10000,
  maxRecursiveEmit: 1000,
  maxUnsubscribeAllLoop: 1000,
});

onFirstSubscription

A function called when the number of subscribers goes from 0 to 1

onLastUnsubscribe

A function called when the number of subscribers goes from 1 to 0

onDestroy

A function called when the destroy method is called. Note that during this call the Subscription is already destroyed and you can't call emit or subscribe anymore.

maxSubscriptionCount

A number to limit the maximum number of simultaneous subscriptions (default is 10000). This limit exist to detect infinit subscription loop.

maxRecursiveEmit

A number to limit the maximum recursive call of emit (default is 1000). This limit exist to detect infinite loop where you emit in a callback.

maxUnsubscribeAllLoop

A number to limit the maximum recursive call of subscribe inside a onUnsubscribe callback (default is 1000).

Testing if a callback / subId is subscribed

The isSubscribed[ById] methods let you test whether or not a callback / subId is currently subscribed

subscription.isSubscribed(myCallback); // <- boolean
subscription.isSubscribedById('my-sub-id'); // <- boolean

Reading the number of active Subscriptions

You can call the size method to get the number of subscriptions.

subscription.size();

Destroying a Subscription

You can call the destroy method to destroy a subscription. This will unsubscribe all callback and call the onDestroy option if any.

subscription.destroy();

Once destroyed, calling emit or subscribe[ById] will throw an error. You can still call the other methods but they will have no effect.

You can check if a subscription is destroyed by calling the isDestroyed method.

subscription.isDestroyed(); // <- boolean

Some precisions

Callback are called in the order they are subscribed.

If you re-subscribe the same callback or id it will not re-do a subscription but instead move the subscription to the end.

In other words, calling subscribe on an already subscribed callback or subId will not make the callback called twice. But it will move the callback at the end of the subscription list. In the case of a subId, the callback will be replaced by the new one.

If you call unsubscribe in a callback it will have effect immediatly.

If the callback you unsubscribe is supposed to run after the current callback, it will not be called.

If you subscribe in a callback it will not be called immediatly.

But it will be in the next emit.

If you emit() in a callback it will defer the call to after the current emit is done.

If you subscribe / unsubscribe / emit in an onUnsubscribed it will behave the same as if it was in the callback itself

Calling destroy will unsubscribe all callback and call the onUnsubscribe if any

In these onUnsubscribe callback the subscription is considered destroyed so you can't call emit or subscribe anymore.

Calling destroy on a destroyed subscription will have no effect

This is a no-op, it will not call onDestroy again.

The subscription is already considered destroyed when onDestroy is called

This means that you can't call emit or subscribe in the onDestroy callback and that isDestroyed will return true in the onDestroy callback.

Scheduler [ADVANCED]

At the core of the Subscription is a scheduler that will manage the different callbacks and their order of execution. If you need a single subscription or event multiple that don't interact with each other, you don't need to know about the scheduler. But if you need for example to subscribe to a subscription in the callback of another subscription then keep reading.

Resuse the same scheduler for multiple subscriptions

You can create a Scheduler unsing the createScheduler function. You can then pass this scheduler as the first option of the createSubscription and createVoidSubscription functions.

import { createScheduler, createSubscription } from '@dldc/pubsub';

const scheduler = createScheduler();

const sub1 = createSubscription(scheduler);
const sub2 = createSubscription(scheduler);

Note that the createScheduler function accept the same options as the createSubscription function. When you pass a scheduler to create a subscription, you can also pass a second argument to specify a onFirstSubscription and onLastUnsubscribe function specific to this subscription.

import { createScheduler, createSubscription } from '@dldc/pubsub';

const scheduler = createScheduler();

const sub1 = createSubscription(scheduler, {
  onFirstSubscription: () => {
    console.log('First subscription');
  },
  onLastUnsubscribe: () => {
    console.log('Last unsubscribe');
  },
});

Destrying a scheduler

Note that when you destroy a scheduler, all subscriptions that use this scheduler will be destroyed as well. Calling .destroy() on a subscription will actually call .destroy() on the scheduler.

API

export type Unsubscribe = () => void;
export type OnUnsubscribed = () => void;
export type SubscriptionCallback<T> = (value: T) => void;
export type VoidSubscriptionCallback = () => void;
export type UnsubscribeAllMethod = () => void;

export interface SubscribeMethod<T> {
  (callback: SubscriptionCallback<T>, onUnsubscribe?: OnUnsubscribed): Unsubscribe;
  (subId: string, callback: SubscriptionCallback<T>, onUnsubscribe?: OnUnsubscribed): Unsubscribe;
}

export interface VoidSubscribeMethod {
  (callback: VoidSubscriptionCallback, onUnsubscribe?: OnUnsubscribed): Unsubscribe;
  (subId: string, callback: VoidSubscriptionCallback, onUnsubscribe?: OnUnsubscribed): Unsubscribe;
}

export interface IsSubscribedMethod<T> {
  (subId: string): boolean;
  (callback: SubscriptionCallback<T>): boolean;
}

export interface UnsubscribeMethod<T> {
  (subId: string): void;
  (callback: SubscriptionCallback<T>): void;
}

export interface VoidIsSubscribedMethod {
  (subId: string): boolean;
  (callback: VoidSubscriptionCallback): boolean;
}

export interface VoidUnsubscribeMethod {
  (subId: string): void;
  (callback: VoidSubscriptionCallback): void;
}

export interface ISubscription<T> {
  subscribe: SubscribeMethod<T>;
  unsubscribe: UnsubscribeMethod<T>;
  unsubscribeAll: UnsubscribeAllMethod;
  isSubscribed: IsSubscribedMethod<T>;
  size: () => number;
  emit: (newValue: T) => void;
  destroy: () => void;
  isDestroyed: () => boolean;
}

export interface IVoidSubscription {
  subscribe: VoidSubscribeMethod;
  unsubscribe: VoidUnsubscribeMethod;
  unsubscribeAll: UnsubscribeAllMethod;
  isSubscribed: VoidIsSubscribedMethod;
  size: () => number;
  emit: () => void;
  destroy: () => void;
  isDestroyed: () => boolean;
}

export interface ISubscriptionOptions {
  onFirstSubscription?: () => void;
  onLastUnsubscribe?: () => void;
  onDestroy?: () => void;
  maxSubscriptionCount?: number;
  maxRecursiveEmit?: number;
}