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

lux-callback-emitter

v1.0.1

Published

Typable event emitter system based on callback functions

Downloads

31

Readme

Why need another customized Event Emitter?

While EventEmitters are a great invention, in my opinion TypeScript still lacks a suitable way to implement a flexible, socket.io-like registration and de-registration of user-defined events in as many application areas as possible. The CallbackEmitter class is my personal attempted solution to this problem: It not only handles the emitting or invoking, registration and unregistration of callback handlers reliably (without the annoying double registration problems of e.g. addEventHandler) and extends this functionality with the handy Once of socket.io as well as full and dynamic TypeScript support.

Some of the main strengths include:

  • Dynamic typing and callback signature support
  • Registration by reference to prevent duplicate listeners
  • Listeners to dynamically enable or disable a callback without the need of repeatedly calling On and Off
  • Handler return value collection

Reference

CallbackEmitter::On

Registers a callback handler for an event name. Registration is done by reference, so multiple registrations won't change anything.

|Parameter|Description| |-|-| |event : string|Event name literal| |callback : function|Handler function. Will be executed once the event emits|

If using TypeScript in combination with a CallbackEmitterEventMap type, the event name will influence the required callback signature respecting the event map.

CallbackEmitter::Off

Unregisters a callback handler for an event name.

|Parameter|Description| |-|-| |event : string|Event name literal| |callback : function|Handler function to unregister|

CallbackEmitter::Once

Analogous to the CallbackEmitter::On functionality, with the exception that it is immediately unregistered once it has been invoked, thus only being emitted once.

NOTICE: This will not register the provided handler by reference, which may result in duplicate or multiple registrations.

CallbackEmitter::CountListenersOnEvent

Returns a number type representing the current amount of registered handler functions on the given event literal. Please note that this will include Once registrations.

|Parameter|Description| |-|-| |event : string|Event name literal|

CallbackEmitter::Emit

Emits an event by it's literal. When using a CallbackEmitterEventMap, the parameters required depend on the handler type declaration for the respective event literal.

If the handler functions return anything, all return values are collected (yes, even those from promises) and returned to the caller as any[] after all handlers have been invoked.

|Parameter|Description| |-|-| |event : string|Event name literal| |...params : any[]|Parameters to pass through handler functions|

CallbackEmitter::GetListener

Once more, this method is analogous to CallbackEmitter:On, yet instead of registering the supplied callback in silence, it returns a new CallbackEmitterListener object for the specified event literal.

Types

CallbackEmitterEventMap

Event maps are essential for keeping emitter instances organized and handlers structured correctly. While any CallbackEmitter can be used without it, providing an event map when constructing the emitter class is the only way to use advanced type linting and autocompletion features.

Example

/**
 * "CallbackEmitterEventMap"s are object types that declare 
 * the handler function signatures for each event literal
 */
type SomeCoolMap =
	{
		userLogon: (uid: symbol, name: string) => void;
		userLogoff: (uid: symbol) => void;
		chatMessage: (uidFrom: symbol, msg: string) => void;
	}

const chatEmitter = new CallbackEmitter<SomeCoolMap>;

/**
 * The use of chatEmitter.On() and other methods will now be 
 * typed according to their literal assigned function signatures.
 **/

CallbackEmitterListener

A listener is an object that contains only one boolean field called enabled. Updating this field will either register or unregister the callback handler, while defaulting to true (thus registered) at the start. This can be useful in any context where anonymous functions are required or Once/Off are inapplicable options. A particularly common usage is in conjunction with React hooks.

Example

const chatEmitter = new CallbackEmitter<SomeCoolMap>;

function MyReactComponent(props)
{
	const [chatMessages, setChatMessages] = useEffect([]);
	
	useEffect(() => {
		// Create a listener using the recent state update function
		const messageListener = 
			chatEmitter.GetListener("chatMessage", (id, msg) => {
				setChatMessages([...chatMessages, {id, msg}]);
			});

		// Return an anonymous deactivation function to execute
		//   once the state updates and the effect re-evaluates
		return () => messageListener.enabled = false;
	}, [chatMessages])

	return <div className="chat-box">
		{chatMessages.map(someCoolMessageFormatter)}
	</div>
}

Yeah I know, this example seems a bit silly. In reality, the listener functionality would be considerably more beneficial within React class components, for instance. Nevertheless, for the sake of simplicity, this example serves to illustrate the point.

CallbackEmitterOnFunc and CallbackEmitterEmitFunc

These generic types have been created to streamline the process for developers attempting to enhance the functionality of a CallbackEmitter from within a custom class or library. They perform the tasks of dynamic typing and function signature matching, eliminating the need for developers to worry about these details.

Example

class OnlyAllowOnButNotEmitBecauseIWantToDoItMyselfClass
{
	#emitter = new CallbackEmitter<SomeCoolMap>;

	public On: CallbackEmitterOnFunc<SomeCoolMap> = 
		(...a) => this.#emitter.On(...a)
}

class OnlyAllowEmitBecauseThatDoesNotMykeAnyDamnSenseClass
{
	#emitter = new CallbackEmitter<SomeCoolMap>;

	public Emit: CallbackEmitterEmitFunc<SomeCoolMap> = 
		(...a) => this.#emitter.On(...a)
}