export interface Consumer { push(a: A, changed?: any): void; } export class NoopConsumer implements Consumer { push(): void {}; } export const noopConsumer = new NoopConsumer(); export class MultiConsumer implements Consumer { listeners: Consumer[]; constructor(c1: Consumer, c2: Consumer) { this.listeners = [c1, c2]; } push(a: A): void { for (let i = 0; i < this.listeners.length; ++i) { this.listeners[i].push(a); } } } export abstract class Reactive { child: Consumer; nrOfListeners: number; constructor() { this.child = noopConsumer; this.nrOfListeners = 0; } subscribe(fn: (a: A) => void): Consumer { const listener = {push: fn}; this.addListener(listener); return listener; } addListener(c: Consumer): void { const nr = ++this.nrOfListeners; if (nr === 1) { this.child = c; } else if (nr === 2) { this.child = new MultiConsumer(this.child, c); } else { (>this.child).listeners.push(c); } } removeListener(listener: Consumer): void { const nr = --this.nrOfListeners; if (nr === 0) { this.child = noopConsumer; } else if (nr === 1) { const l = (>this.child).listeners; this.child = l[l[0] === listener ? 1 : 0]; } else { const l = (>this.child).listeners; // The indexOf here is O(n), where n is the number of listeners, // if using a linked list it should be possible to perform the // unsubscribe operation in constant time. const idx = l.indexOf(listener); if (idx !== -1) { if (idx !== l.length - 1) { l[idx] = l[l.length - 1]; } l.length--; // remove the last element of the list } } } } /** * Things capable of observing behaviors */ export interface Observer { beginPulling(): void; endPulling(): void; push(a: A): void; } export class MultiObserver implements Observer { listeners: Observer[]; constructor(c1: Observer, c2: Observer) { this.listeners = [c1, c2]; } beginPulling(): void { for (let i = 0; i < this.listeners.length; ++i) { this.listeners[i].beginPulling(); } } endPulling(): void { for (let i = 0; i < this.listeners.length; ++i) { this.listeners[i].endPulling(); } } push(a: A): void { for (let i = 0; i < this.listeners.length; ++i) { this.listeners[i].push(a); } } } class NoopObserver implements Observer { beginPulling(): void {} endPulling(): void {} push(a: A): void {} } export const noopObserver: Observer = new NoopObserver();