Remove Tappable
and Emitter
This commit is contained in:
parent
391958f5cf
commit
867cf51acb
4 changed files with 1 additions and 188 deletions
|
@ -254,7 +254,7 @@ class PrismInstance<V> implements Prism<V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a tappable that fires every time the prism's state goes from `fresh-\>stale.`
|
* Calls `callback` every time the prism's state goes from `fresh-\>stale.` Returns an `unsubscribe()` function.
|
||||||
*/
|
*/
|
||||||
onStale(callback: () => void): VoidFn {
|
onStale(callback: () => void): VoidFn {
|
||||||
const untap = () => {
|
const untap = () => {
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
import Emitter from './Emitter'
|
|
||||||
|
|
||||||
describe('DataVerse.Emitter', () => {
|
|
||||||
it('should work', () => {
|
|
||||||
const e: Emitter<string> = new Emitter()
|
|
||||||
e.emit('no one will see this')
|
|
||||||
e.emit('nor this')
|
|
||||||
|
|
||||||
const tappedEvents: string[] = []
|
|
||||||
const untap = e.tappable.tap((payload) => {
|
|
||||||
tappedEvents.push(payload)
|
|
||||||
})
|
|
||||||
e.emit('foo')
|
|
||||||
e.emit('bar')
|
|
||||||
untap()
|
|
||||||
e.emit('baz')
|
|
||||||
expect(tappedEvents).toMatchObject(['foo', 'bar'])
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,76 +0,0 @@
|
||||||
import Tappable from './Tappable'
|
|
||||||
|
|
||||||
type Tapper<V> = (v: V) => void
|
|
||||||
type Untap = () => void
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An event emitter. Emit events that others can tap (subscribe to).
|
|
||||||
*/
|
|
||||||
export default class Emitter<V> {
|
|
||||||
private _tappers: Map<any, (v: V) => void>
|
|
||||||
private _lastTapperId: number
|
|
||||||
private _onNumberOfTappersChangeListener: undefined | ((n: number) => void)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Tappable associated with this emitter. You can use this to tap (subscribe to) events emitted.
|
|
||||||
*/
|
|
||||||
readonly tappable: Tappable<V>
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this._lastTapperId = 0
|
|
||||||
this._tappers = new Map()
|
|
||||||
this.tappable = new Tappable({
|
|
||||||
tapToSource: (cb: Tapper<V>) => this._tap(cb),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
_tap(cb: Tapper<V>): Untap {
|
|
||||||
const tapperId = this._lastTapperId++
|
|
||||||
this._tappers.set(tapperId, cb)
|
|
||||||
this._onNumberOfTappersChangeListener?.(this._tappers.size)
|
|
||||||
return () => {
|
|
||||||
this._removeTapperById(tapperId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_removeTapperById(id: number) {
|
|
||||||
const oldSize = this._tappers.size
|
|
||||||
this._tappers.delete(id)
|
|
||||||
const newSize = this._tappers.size
|
|
||||||
if (oldSize !== newSize) {
|
|
||||||
this._onNumberOfTappersChangeListener?.(newSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emit a value.
|
|
||||||
*
|
|
||||||
* @param payload - The value to be emitted.
|
|
||||||
*/
|
|
||||||
emit(payload: V) {
|
|
||||||
for (const cb of this._tappers.values()) {
|
|
||||||
cb(payload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the emitter has tappers (subscribers).
|
|
||||||
*/
|
|
||||||
hasTappers() {
|
|
||||||
return this._tappers.size !== 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls callback when the number of tappers (subscribers) changes.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```ts
|
|
||||||
* emitter.onNumberOfTappersChange((n) => {
|
|
||||||
* console.log("number of tappers changed:", n)
|
|
||||||
* })
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
onNumberOfTappersChange(cb: (n: number) => void) {
|
|
||||||
this._onNumberOfTappersChangeListener = cb
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
type Untap = () => void
|
|
||||||
type UntapFromSource = () => void
|
|
||||||
|
|
||||||
interface IProps<V> {
|
|
||||||
tapToSource: (cb: (payload: V) => void) => UntapFromSource
|
|
||||||
}
|
|
||||||
|
|
||||||
type Listener<V> = ((v: V) => void) | (() => void)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a data-source that can be tapped (subscribed to).
|
|
||||||
*/
|
|
||||||
export default class Tappable<V> {
|
|
||||||
private _props: IProps<V>
|
|
||||||
private _tappers: Map<number, {bivarianceHack(v: V): void}['bivarianceHack']>
|
|
||||||
private _untapFromSource: null | UntapFromSource
|
|
||||||
private _lastTapperId: number
|
|
||||||
private _untapFromSourceTimeout: null | NodeJS.Timer = null
|
|
||||||
|
|
||||||
constructor(props: IProps<V>) {
|
|
||||||
this._lastTapperId = 0
|
|
||||||
this._untapFromSource = null
|
|
||||||
this._props = props
|
|
||||||
this._tappers = new Map()
|
|
||||||
}
|
|
||||||
|
|
||||||
private _check() {
|
|
||||||
if (this._untapFromSource) {
|
|
||||||
if (this._tappers.size === 0) {
|
|
||||||
this._scheduleToUntapFromSource()
|
|
||||||
/*
|
|
||||||
* this._untapFromSource()
|
|
||||||
* this._untapFromSource = null
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this._tappers.size !== 0) {
|
|
||||||
this._untapFromSource = this._props.tapToSource(this._cb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _scheduleToUntapFromSource() {
|
|
||||||
if (this._untapFromSourceTimeout !== null) return
|
|
||||||
this._untapFromSourceTimeout = setTimeout(() => {
|
|
||||||
this._untapFromSourceTimeout = null
|
|
||||||
if (this._tappers.size === 0) {
|
|
||||||
this._untapFromSource!()
|
|
||||||
|
|
||||||
this._untapFromSource = null
|
|
||||||
}
|
|
||||||
}, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
private _cb: any = (arg: any): void => {
|
|
||||||
for (const cb of this._tappers.values()) {
|
|
||||||
cb(arg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tap (subscribe to) the data source.
|
|
||||||
*
|
|
||||||
* @param cb - The callback to be called on a change.
|
|
||||||
*/
|
|
||||||
tap(cb: Listener<V>): Untap {
|
|
||||||
const tapperId = this._lastTapperId++
|
|
||||||
this._tappers.set(tapperId, cb)
|
|
||||||
this._check()
|
|
||||||
return () => {
|
|
||||||
this._removeTapperById(tapperId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _removeTapperById(id: number) {
|
|
||||||
this._tappers.delete(id)
|
|
||||||
this._check()
|
|
||||||
}
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @deprecated
|
|
||||||
// */
|
|
||||||
// map<T>(transform: {bivarianceHack(v: V): T}['bivarianceHack']): Tappable<T> {
|
|
||||||
// return new Tappable({
|
|
||||||
// tapToSource: (cb: (v: T) => void) => {
|
|
||||||
// return this.tap((v: $IntentionalAny) => {
|
|
||||||
// return cb(transform(v))
|
|
||||||
// })
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
}
|
|
Loading…
Reference in a new issue