@axijs/emitter
v1.2.0
Published
A minimalistic, type-safe library for single-event and state emitting
Readme
@axijs/emitter
A minimalistic, type-safe library for single-event and state emitting.
Inspired by the Observer pattern and RxJS (like BehaviorSubject),
it provides a clean way to manage subscriptions.
Installation
npm install @axijs/emitter
# or
pnpm add @axijs/emitter
# or
yarn add @axijs/emitterFeatures
- Strictly Typed: Full TypeScript support for emitted values.
- No Magic Strings: Object-based emitters instead of string keys (e.g.,
on('event-name')). - State Emitting:
StateEmitterremembers the last emitted value and immediately triggers new subscribers. - Distinct Updates:
StateEmittercan optionally skip emitting unchanged values. - Composite Subscriptions: Easily group multiple subscriptions and teardown logic into a single
Subscriptionobject to prevent memory leaks.
Usage
1. Basic Event Emitter
Create an isolated, strongly-typed event emitter.
import { Emitter } from '@axijs/emitter';
const onPlayerMove = new Emitter<string>();
const sub = onPlayerMove.subscribe((player) => {
console.log(`Player moved: ${player}`);
});
onPlayerMove.emit('Alice');
onPlayerMove.once((player) => {
console.log('This will only run once!');
});
sub.unsubscribe();2. State Emitter
Acts like a state container (similar to BehaviorSubject).
It holds the latest value and immediately provides it to new subscribers.
import { StateEmitter } from '@axijs/emitter';
const health = new StateEmitter<number>(100);
health.subscribe((currentHealth) => {
console.log(`Health is: ${currentHealth}`); // Immediately logs: "Health is: 100"
});
health.emit(80); // Logs: "Health is: 80"
console.log(health.value); // 80Distinct Values & Custom Comparison
You can prevent the emitter from firing consecutively with the same value by using the StateEmitterOptions.
export interface StateEmitterOptions<T> {
distinct?: boolean;
compare?: (prev: T, next: T) => boolean;
}When distinct: true is set, the emitter checks if the new value is the same as the previous one.
By default, it uses simple reference equality (Object.is), which is fast and works perfectly for primitives.
// Prevents emitting the same primitive value twice
const status = new StateEmitter('idle', { distinct: true });
status.emit('idle'); // IgnoredFor complex objects, or if you want to apply custom comparison logic, you can provide a compare callback.
This is useful when you only care about specific fields changing (like an id) or when using a deep equal function from a library.
interface User {
id: number;
name: string;
}
const activeUser = new StateEmitter<User>(
{ id: 1, name: 'Alice' },
{
distinct: true,
// Custom logic: objects are considered the same if their IDs match
compare: (prev, next) => prev.id === next.id
}
);
activeUser.subscribe(user => console.log('User changed:', user.name));
// Will NOT emit, because the 'id' is still 1
activeUser.emit({ id: 1, name: 'Alice Updated' });
// WILL emit, because the 'id' has changed
activeUser.emit({ id: 2, name: 'Bob' });3. Composite Subscriptions
Group multiple unsubscriptions together. Very useful for cleaning up UI components or game objects.
import { Emitter, Subscription } from '@axijs/emitter';
const onJump = new Emitter<void>();
const onShoot = new Emitter<void>();
const masterSub = new Subscription();
// Add multiple subscriptions to the master Subscription
masterSub.add(onJump.subscribe(() => console.log('Jumped!')));
masterSub.add(onShoot.subscribe(() => console.log('Pew pew!')));
// You can also add custom teardown functions
masterSub.add(() => {
console.log('Custom cleanup logic executed');
});
// Later, when the component/object is destroyed:
masterSub.unsubscribe();
// This automatically unsubscribes from both events and runs the custom logicAPI Documentation
For a complete list of classes, interfaces, and methods, please visit the API Documentation.
License
MIT
