UNPKG

@nozbe/watermelondb

Version:

Build powerful React Native and React web apps that scale from hundreds to tens of thousands of records and remain fast

70 lines (55 loc) 2.01 kB
// @flow import { type Unsubscribe } from '../type' import invariant from '../../common/invariant' // A subscribable that implements the equivalent of: // multicast(() => new ReplaySubject(1)) |> refCount Rx operation // // In other words: // - Upon subscription, the source subscribable is subscribed to, // and its notifications are passed to subscribers here. // - Multiple subscribers only cause a single subscription of the source // - When last subscriber unsubscribes, the source is unsubscribed // - Upon subscription, the subscriber receives last value sent by source (if any) export default class SharedSubscribable<T> { _source: (subscriber: (T) => void) => Unsubscribe _unsubscribeSource: ?Unsubscribe = null _subscribers: [(T) => void, any][] = [] _didEmit: boolean = false _lastValue: T = (null: any) constructor(source: (subscriber: (T) => void) => Unsubscribe): void { this._source = source } subscribe(subscriber: (T) => void, debugInfo?: any): Unsubscribe { const entry = [subscriber, debugInfo] this._subscribers.push(entry) if (this._didEmit) { subscriber(this._lastValue) } if (this._subscribers.length === 1) { // TODO: What if this throws? this._unsubscribeSource = this._source((value) => this._notify(value)) } return () => this._unsubscribe(entry) } _notify(value: T): void { invariant( this._subscribers.length, `SharedSubscribable's source emitted a value after it was unsubscribed from`, ) this._didEmit = true this._lastValue = value this._subscribers.forEach(([subscriber]) => { subscriber(value) }) } _unsubscribe(entry: [(T) => void, any]): void { const idx = this._subscribers.indexOf(entry) idx !== -1 && this._subscribers.splice(idx, 1) if (!this._subscribers.length) { const unsubscribe = this._unsubscribeSource this._unsubscribeSource = null this._didEmit = false unsubscribe && unsubscribe() } } }