lux-callback-emitter
TypeScript icon, indicating that this package has built-in type declarations

1.0.1 • Public • Published

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)
}

Readme

Keywords

none

Package Sidebar

Install

npm i lux-callback-emitter

Weekly Downloads

4

Version

1.0.1

License

MIT

Unpacked Size

12.4 kB

Total Files

4

Last publish

Collaborators

  • thelaumix