Popularity
0.2
Declining
Activity
7.8
Growing
3
2
0

Description

JavaScript implementation of the Publish/Subscribe pattern

Monthly Downloads: 0
Programming language: JavaScript
Tags: Loaders     Event     Emitter     Pubsub     Publish-subscribe     Event-emitter    
Latest version: v1.1.4

Publish / Subscribe alternatives and similar libraries

Based on the "Loaders" category

Do you think we are missing an alternative of Publish / Subscribe or a related project?

Add another 'Loaders' Library

README

Publish Subscribe

JavaScript implementation of the Publish-Subscribe pattern.

๐Ÿ—Ž Publish Subscribe Documentation ๐Ÿ—Ž

NPM Version NPM Downloads Build Status Issues

Known Vulnerabilities Dependency Status devDependencies Status

GitHub stars GitHub watchers GitHub followers GitHub forks


If you use this project don't forget to give a โญ star โญ to it on GitHub!


๐Ÿƒ๐Ÿ’จ Tl;dr

import { PublishSubscribe } from "@r37r0m0d3l/publish_subscribe/es";
//
const pubsub: PublishSubscribe = new PublishSubscribe();
type IChannel = number|string|symbol;
const CHANNEL: IChannel = "app";
//
const isCalledOnce: boolean = false;
function syncCallback(data: any, channel: IChannel, token: string): any {
  return true;
}
async function asyncCallback(
  data: any,
  channel: IChannel,
  token: string
): any {
  return true;
}
const tokenSync: string = pubsub
  .subscribe(CHANNEL, syncCallback, isCalledOnce);
const tokenAsync: string = pubsub
  .subscribe(CHANNEL, syncCallback, isCalledOnce);
//
const data: any = { someData: "to publish" };
const resultOnly: boolean = true;
const cloneData: boolean = true;
function callback(
  results: Array<any | { channel: IChannel; data: any; token?: string }>
): any {}
const resultsSync = pubsub
  .publishSync(CHANNEL, data, resultOnly, cloneData, callback);
(async () => {
  const resultsAsync = await pubsub
    .publishAsync(CHANNEL, data, resultOnly, cloneData);
})();

๐Ÿ“ฆ Usage

Installation.

npm install @r37r0m0d3l/publish_subscribe

CommonJS.

const PublishSubscribe = require("@r37r0m0d3l/publish_subscribe").default;
const { PublishSubscribe } = require("@r37r0m0d3l/publish_subscribe");

ECMAScript Modules.

import { PublishSubscribe } from "@r37r0m0d3l/publish_subscribe/es";

โŒจ Creating Instance

const pubsub = new PublishSubscribe();

โ„น Logging

Set log function.

pubsub.setLogging((info) => {
  console.group("LOGGING!!!");
  console.log(info);
  /*
  Example when subscribing:
  {
    channel: "app",
    callback: [Function: sync],
    once: false,
    method: "subscribe"
  }
  Example when publishing:
  {
    channel: "app",
    data: { data: "the data" },
    token: "CETl6gZXkTMhm7JY",
    method: "publishSync -> send"
  }
  */
  console.groupEnd();
});

pubsub.disableLogging();

๐Ÿ“› Naming Channels

Channels can be finite numbers, strings or symbols.

const CHANNEL_NAME_AS_STRING = "TheChannelName";
const CHANNEL_NAME_AS_NUMBER = 123;
const CHANNEL_NAME_AS_SYMBOL = Symbol("ThisChannelLookVeryPrivate");
const CHANNEL = "channel_name";

๐Ÿ“ฉ Subscription

Synchronous subscription.

const tokenOfSynchronous = pubsub
  .subscribe("channel_name", function sync(data, channel, token) {
    return {
      message: "Here can by any kind data for synchronous functions"
    };
  });

Asynchronous subscription.

const tokenOfAsynchronous = pubsub
  .subscribe("channel_name", async (data, channel, token) => {
    // Other async call here
    return "Here can by any kind data for asynchronous functions";
  });

Reusable subscription.

/**
 * @param {*} data
 * @param {number|string|Symbol} channel
 * @param {string} token
 */
function synchronousCallback(data, channel, token) {
  // And here is no any data returned if you wish
}
const tokenOne = pubsub.subscribe("channel_name_1", synchronousCallback);
const tokenTwo = pubsub.subscribe("channel_name_2", synchronousCallback);

No need for token as subscription will expire after first successful call.

pubsub.subscribeOnce("channel_exit", (data, channel, _token) => {});

๐Ÿ“จ Publishing Events

Intercept all publishing.

pubsub.onPublish((channel, data) => {
  // All publishing will be intercepted here
  // and you can do your "message matching" here
  switch(channel) {
    case "app:logout":
      pubsub.dropAll();
    break;
  }
});

pubsub.onPublish(); // now its disabled

Get synchronous results from subscribers.

// Channel name
const channel = "app:start";
// Any kind of data - primitive or object
const data = { data: "the data" };
// Default behavior
const resultOnly = true;
// Prevent published data changes between subscriptions
const cloneData = true;
// Array with results
const results = pubsub
  .publishSync(channel, data, resultOnly, cloneData, (results) => {
    // Only synchronous functions will be called!
    // Array of results can be intercepted in callback
    console.log(results);
  });
console.log(results); // or can be accessed as function call

Get synchronous and asynchronous results from subscribers.

pubsub
  .publishAsync("channel_name", { data: "the data" }, false)
    .then((results) => {
      // results here also contains
      // additional array of data from subscribers
      // [ { channel: "app", result: "data", token: "zzroUP97lnxL0VUa" } ]
    });

Do not wait anything from subscribers - use publish.

pubsub.publish("channel_name", { data: "the data" });

๐Ÿ“ช Unsubscribe

Unsubscribe via token previously retrieved from subscribe method.

const token = pubsub.subscribe("channel_name", () => {});
pubsub.unsubscribeByToken(token);

Unsubscribe by channel and callback.

All subscriptions in channel based on callback will be removed.

// callback == function someFunction(data, channel, token) {}
pubsub.unsubscribeByChannelAndCallback("channel_name", callback);

This callback function will be removed from all subscriptions.

pubsub.unsubscribeByCallback(callback);

Unsubscribe based on parameters provided.

pubsub.unsubscribe(callbackOrChannelOrToken);
pubsub.unsubscribe(channel, callback);

๐Ÿ› ๏ธ Utilities

Retrieve callback function via token.

// token == "zzroUP97lnxL0VUa"
const callback = pubsub.getCallback(token);

Has any subscriptions.

pubsub.hasSubscription("channel_name");

Has channel.

pubsub.hasChannel("channel_name");

Get list of channels. Array of numbers, strings, symbols.

pubsub.getChannels();

Drop channel.

pubsub.dropChannel("channel_name");

Clear instance.

pubsub.dropAll();

๐Ÿคท About

Yet another publish-subscribe library? Why this library exists:

  • โญโญโญ

    • Preventing published data changes between subscriptions.
    • Possibility to publish events asynchronously.
    • Possibility to do synchronous publishing and receive data from subscriptions.
    • Possibility to use async callbacks in subscriptions.
    • Get subscription callback function by token and use it somewhere else.
    • Error swallowing. Publish-subscribe pattern should not broke on some function somewhere deep in code.
  • โญโญ

    • Possibility to use finite numbers, strings or symbols as channel names.
    • Subscription functions receive callback token among channel name and published data.
    • Has subscribeOnce method.
    • Has onPublish method for global message matching.
  • โญ

    • Custom logging into console for most actions.
    • TypeScript definitions.
    • Almost dependency free. This library will work even if the last commit was ten years ago.

The worst things that can happen to publish-subscribe library that is not here:

  • โ˜ ๏ธโ˜ ๏ธโ˜ ๏ธ

    • You should have multiple subscriptions for one channel. Somehow it does not exists in other libraries.
    • Event inheritance. This should be handled by subscription callbacks not by the publish-subscribe system.
    • Hellish wildcards-hierarchical addressing-messages matching for each event. First of all this is impossible with numbers or symbols. And this makes no sense as you can create root channel name anyway. In example when you publish "app:user:registered" and "app:user:login", just publish "app:user" among others. While when you publish "app:user:re-login", do not publish "app:user" and "re-login" event will be kept private or unimportant to others.
  • โ˜ ๏ธโ˜ ๏ธ

    • Possibility to define context for each callback. You have arrow functions for that. You should not save context in one place and take it hostage. Probably you should use global variables instead etc.
    • Cancel publish event distribution for subscribers. This behavior reserved for Observer pattern.
  • โ˜ ๏ธ

    • No order priority for subscriptions. Somehow pattern should be about decoupling, but sometimes it has it in.

The things you may not like:

  • ๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ

    • No ECMAScript 3 / ECMAScript 5 / Internet Explorer 6 compatibility. You can transpile library to CommonJS module via Babel with configuration you need.

๐Ÿ‘€ Discover more

  • ๐Ÿ”ŽConsono๐Ÿ”Ž - The most informative and correct variable inspector for JavaScript on the web.

  • ๐ŸŒ OF๐ŸŒ  - Promise wrapper with some sugar.

  • ๐Ÿ”ฉLocal Storage Fallback๐Ÿ”ฉ - Universal localStorage fallback.

  • ๐ŸงฐVicis๐Ÿงฐ - Presentation and transformation layer for data output in RESTful APIs.

Or find useful these tools:

  • ๐ŸงพJSON Sorter๐Ÿงพ - Neat online JSON sorter.