1
0
mirror of https://github.com/robonen/tools.git synced 2026-03-20 10:54:44 +00:00

Merge pull request #3 from robonen/stdlib-pubsub

Stdlib pubsub
This commit is contained in:
2024-04-16 17:07:14 +07:00
committed by GitHub
4 changed files with 156 additions and 1 deletions

View File

@@ -1,2 +1,3 @@
export * from './text';
export * from './math';
export * from './math';
export * from './patterns';

View File

@@ -0,0 +1,95 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { PubSub } from './index';
describe('PubSub', () => {
let eventBus: PubSub<{
event1: (arg: string) => void;
event2: () => void
}>;
beforeEach(() => {
eventBus = new PubSub();
});
it('add a listener and emit an event', () => {
const listener = vi.fn();
eventBus.on('event1', listener);
eventBus.emit('event1', 'Hello');
expect(listener).toHaveBeenCalledWith('Hello');
});
it('add multiple listeners and emit an event', () => {
const listener1 = vi.fn();
const listener2 = vi.fn();
eventBus.on('event1', listener1);
eventBus.on('event1', listener2);
eventBus.emit('event1', 'Hello');
expect(listener1).toHaveBeenCalledWith('Hello');
expect(listener2).toHaveBeenCalledWith('Hello');
});
it('add a one-time listener and emit an event', () => {
const listener = vi.fn();
eventBus.once('event1', listener);
eventBus.emit('event1', 'Hello');
eventBus.emit('event1', 'World');
expect(listener).toHaveBeenCalledWith('Hello');
expect(listener).not.toHaveBeenCalledWith('World');
});
it('add once listener and emit multiple events', () => {
const listener = vi.fn();
eventBus.once('event1', listener);
eventBus.emit('event1', 'Hello');
eventBus.emit('event1', 'World');
eventBus.emit('event1', '!');
expect(listener).toHaveBeenCalledTimes(1);
expect(listener).toHaveBeenCalledWith('Hello');
});
it('remove a listener', () => {
const listener = vi.fn();
eventBus.on('event1', listener);
eventBus.off('event1', listener);
eventBus.emit('event1', 'Hello');
expect(listener).not.toHaveBeenCalled();
});
it('clear all listeners for an event', () => {
const listener1 = vi.fn();
const listener2 = vi.fn();
eventBus.on('event1', listener1);
eventBus.on('event1', listener2);
eventBus.clear('event1');
eventBus.emit('event1', 'Hello');
expect(listener1).not.toHaveBeenCalled();
expect(listener2).not.toHaveBeenCalled();
});
it('return true when emitting an event with listeners', () => {
const listener = vi.fn();
eventBus.on('event1', listener);
const result = eventBus.emit('event1', 'Hello');
expect(result).toBe(true);
});
it('return false when emitting an event without listeners', () => {
const result = eventBus.emit('event1', 'Hello');
expect(result).toBe(false);
});
});

View File

@@ -0,0 +1,58 @@
export type Subscriber = (...args: any[]) => void;
export type EventsRecord = Record<string, Subscriber>;
export class PubSub<Events extends EventsRecord> {
private events: Map<keyof Events, Set<Events[keyof Events]>>;
constructor() {
this.events = new Map();
}
public on<K extends keyof Events>(event: K, listener: Events[K]) {
const listeners = this.events.get(event);
if (listeners)
listeners.add(listener);
else
this.events.set(event, new Set([listener]));
return this;
}
public off<K extends keyof Events>(event: K, listener: Events[K]) {
const listeners = this.events.get(event);
if (listeners)
listeners.delete(listener);
return this;
}
public once<K extends keyof Events>(event: K, listener: Events[K]) {
const onceListener = (...args: Parameters<Events[K]>) => {
this.off(event, onceListener as Events[K]);
listener(...args);
};
this.on(event, onceListener as Events[K]);
return this;
}
public emit<K extends keyof Events>(event: K, ...args: Parameters<Events[K]>): boolean {
const listeners = this.events.get(event);
if (!listeners)
return false;
listeners.forEach((listener) => listener(...args));
return true;
}
public clear<K extends keyof Events>(event: K) {
this.events.delete(event);
return this;
}
}

View File

@@ -0,0 +1 @@
export * from './behavioral/pubsub';