Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Very hardly typed PubSub, that gives you all autocompletion and auto type validation on both event names and data in callbacks

License

NotificationsYou must be signed in to change notification settings

Kamyil/typed-pubsub

Repository files navigation

Know the events you can publishKnow the data you will receive after subscribing to specific event
Zrzut ekranu 2022-11-17 o 02 01 48Zrzut ekranu 2022-11-17 o 02 02 52

TypicalPubSub,EventBus,EventEmitter (whatever you call it), that you can expect, butfully andhardly typed with full type inference, which means that you will be able to get all autocomplete and autovalidation.It's scalable, very-performant and feature packed with all batteries included withZero dependencies

NPM:https://www.npmjs.com/package/@kamyil/typed-pubsub

GitHub:https://github.com/Kamyil/typed-pubsub

How to use it?

  1. Simply importPubSub from package(since it's named import, VSCode WebStorm and other IDEs should be able to auto-import it easily)
import{PubSub}from'@kamyil/typed-pubsub';
  1. Declare someEvents. Doesn't matter how you call them.The only one important thing is to keep them in one-level nested object. In which way you will declare event names and what data you will pass to them - it's completely up to you
// 1import{PubSub}from'@kamyil/typed-pubsub';// 2constEvents={'user:registered':{firstName:'',lastName:'',age:22},'age:changed':40,'firstName:changed':'Matt','lastName:changed':'Murdock'}
  1. CreatePubSub instance withEvents passed
// 1import{PubSub}from'@kamyil/typed-pubsub';// 2constEvents={'user:registered':{firstName:'',lastName:'',age:22},'age:changed':40,'firstName:changed':'Matt','lastName:changed':'Murdock'};// 3constpubSub=newPubSub({events:Events});
  1. And voilá! You have your PubSub up and running.You can start publishing the events and subscribing to them. With all types auto-infered

Zrzut ekranu 2022-11-17 o 02 01 42

Zrzut ekranu 2022-11-17 o 02 01 48

Zrzut ekranu 2022-11-17 o 02 02 09

Zrzut ekranu 2022-11-17 o 02 02 52

Do I have to declare values on initialization?

You actually don't. Those values just helps TypeScript to inferthe types of your data, so you don't need to create any types for them manually. But if you prefer to declare a types manually, then you can pass the type directly as a generic into the PubSub class like here:

import{PubSub}from'@kamyil/typed-pubsub';typeTEvents={'user:registered':{firstName:string,lastName:string,age:number},'age:changed':number,'firstName:changed':string,'lastName:changed':string}constpubSub=newPubSub<TEvents>({events:{}asTEvents});

But remember that you don't need to do it, since TypeScript will nicely auto infer everything from your usage :)

... so do the values even matter?

Their only role and purpose is to give TypeScript informations to infer, to allow you to have nice type checking, type inference, error validation and autocompletion.As mentioned in previous point, you can even pass the empty object there and it will work in the runtime, but you will loose all of those convinient TypeScript features.If you prefer to declare events and data model as a types - not runtime values, you can declare them like in example in previous point:Do I have to declare values on initialization?

Performance test

You can check how this library is performing by checking this link:https://stackblitz.com/edit/typescript-v2k7gx?file=index.tsTest essentially creates new subscriber on every 10ms, while updating value to all subscribers on every 20ms

Optional logging

For debugging purposes you can enable non-verbose logs

constpubSub=newPubSub({events:Events,enableLogs:true});

Using it with JavaScript

You can also use this library in normal JavaScript files. If you're using VSCode, you should also have type autocompletion enabled by default, even in JS files

What is PubSub?

PubSub is extremely commonpublish-subscribe design pattern that allows you to listen on specific events and retrieve data. Every time you call the.publish() of specific event with some data passed in there, every listener to that event will receive the call and the data you've passed.You can get more info about this here:https://www.enjoyalgorithms.com/blog/publisher-subscriber-pattern

In which way this library is fast?

  1. In compare to other PubSub libraries, this one does not store event listeners in theMap,Array orSet but simple object instead with following model:
{'event1':{1:{eventHandler:someSpecificCallback},2:{eventHandler:differentCallback},// etc.},'event2':{1:{eventHandler:someSpecificCallback},2:{eventHandler:differentCallback},// etc.}// etc.}

It's made this way, because objects are the best store dictionaries for performing heavy reads, since JavaScript engines compile them downto C++ classes which later are being cached.As it's stated here:https://stackoverflow.com/a/49164774

So if you have a write-once read-heavy workload with string keys then you can use an object as a high-performance dictionary

Since PubSub is a pattern where there are not a lot of writes, but there is actually a heavy reading (by using string keys) from it, object seems to be a perfect fit

  1. Also getting all subscribed event listeners is not performed by usingArray.filter() orArray.find() but rather by pointing directly into concrete object, where eventName is a key. So there are no unnecessary loops going on while finding all subscribed event listeners

I want to unsubscribe specific subscriber. How to do it?

Everysubscribe() call returns anunsubscribe() function

constpubSub=newPubSub({events:{testEvent:''}});// you can name it whatever you wantconstunsubscribeTestEvent=pubSub.subscribe('testEvent',()=>{/* ... */});// and you can call it whenever you wantunsubscribeTestEvent();

It's made this way, because this returned unsubsribe function contains id of given event listenerso it's the most proper way to remove this specific listener from the memory

I want to subscribe for one event publish only

You can also set a subscribe listener for only one event publish if needed

constpubSub=newPubSub({events:{testEvent:''}});pubSub.subscribeForOnePublishOnly('testEvent',(data)=>{/** some callback with data */});

I want to clear all subscribers

You can do it by usingclearAllSubscribers() method

pubSub.clearAllSubscribers();

I want to clear subscribers from specific event

You can do it by usingclearAllSubscribersFromEvent(eventName) method,where you can pass the event name as parameter and it will clear all listenersof that event

pubSub.clearAllSubscribersFromEvent('name of your event');

I want to check if there are any active subscribers for specific event

You can do it by usinghasSubscribers() method, which returnstrue if there are subscribersfor passed eventName, andfalse if not.

pubSub.hasSubscribers('name of your event');

I prefer other method names like f.e.emit() &listen() rather thanpublish() &subscribe()

Since this library relies hardly on types, it's hard to add some method rename function,because even if you could run such rename() and start using f.e.pubSub.listen() orpubSub.emit() in the runtime,it unfortunetly wouldn't work properly for types, since TS cannot resolve types dynamicallydepending on your runtime usage. So the solution here(not-ideal but it somewhat solves the problem)would be to create new class that extends this class and remap the names like so:

import{PubSub}from'@kamyil/typed-pubsub';// Remember to add and pass generic here, to let type inference keep working properlyclassEventEmitter<Events>extendsPubSub<Events>{emit=this.publish;listen=this.subscribe;hasListeners=this.hasSubscribers;//... and so on}

Solution is far from ideal, because you will still getpublish() andsubscribe() and othernon-renamed methods in the autocompletion, but at least you will also be able to use those methodswith a new names that you set which fits your preference, while keeping same functionality (in both types and fast execution in the runtime)Probably the better solution would be to copy the source code fromindex.ts, since whole functionality self-contained within one file andrename the methods manually. But if you want to get updates, you will also need to manually update the new code within your copied fileIf you like this library and decide to copy the source code, please at least leave a star on GitHub or install&uninstall it via npm - it will make me surethat this library is used by people and it makes sense to further develop it :)

I want to publish/subscribe asynchronously. How to do it?

Since it doesn't make really sense forsubscribe() to be async, it is designed to be synchronous only.If you want to await for some things within subscribe, you can simply mark it's callback/eventHandler as async

pubSub.subscribe('someEvent',async()=>{awaitsomething();});

However, it is not the case for publish, since there might be some edgy cases where you would want to "pause" further code executionto be completely sure that yourpublish() call will be noticed by all subscribersand all of those subscribers will perform their callbacks before code runs further.publish() method has it's async equivalent namedpublishAsync()It also accepts eventName and data as it's arguments, but in opposite to normal synchronouspublish() it also returnsboolean, that indicates if publish finished successfully, which means - it found all of it's subscribers and (by default, but can be disabled) all of themfinished their callbacks.

However!, if you don't want to await for all subscribers to finish their callbacks, you can disable it by passingawaitAllSubscribersFinish asfalse inoptions paramExample:

asyncfunctiononDataFetched(data){// It will return true or false when all subscribers receive message and all of them finish their callbacksconstisDataPublished=awaitpubSub.publishAsync('data:fetched',data);if(isDataPublished){awaitdoSomethingAfterThePublishIsComplete();awaitdoSomethingElseAfterThePublishIsComplete();}}
asyncfunctiononDataFetched(data){// It will return true or false when all subscribers receive message but may not all of them finish their callbacksconstisDataPublished=awaitpubSub.publishAsync('data:fetched',data,{awaitAllSubscribersFinish:false});if(isDataPublished){awaitdoSomethingAfterThePublishIsComplete();awaitdoSomethingElseAfterThePublishIsComplete();}}

I want to check history of all publishes and subscribes

You can enable history tracking, by enablingkeepHistory option while instantiating PubSub

constpubSub=newPubSub({events:EventskeepHistory:true});

It's a great tool for debugging events. This way you can check history of all publishes, async publishes, subscribes and unsubscribes.And you can log the whole history by running

pubSub.logHistory();

Of course, this option is disabled by default for performance reasons. Not saying that it will badly impact performanceif it will be enabled fe. on production, but it will unnecessarily keep a lot of objects inhistory array for no reason

I want to extend the functionality of it

Since it's a simple class, you can easily extend it by using anextends keyword

exportclassCustomPubSubextendsPubSub{someNewProperty='';someNewFunctionality(){// ...}}

I want to count how many subscribers specific event has

You can do it by usingcountSubscribers() method

pubSub.subscribe('someEvent',()=>{});pubSub.subscribe('someEvent',()=>{});constsubsAmount=pubSub.countSubscribers();console.log(subsAmount)// => 2

About

Very hardly typed PubSub, that gives you all autocompletion and auto type validation on both event names and data in callbacks

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp