UNPKG

kefir

Version:

Reactive Programming library for JavaScript inspired by Bacon.js and RxJS with focus on high performance and low memory usage

239 lines (208 loc) 5.25 kB
import {extend} from './utils/objects' import {VALUE, ERROR, ANY, END} from './constants' import {Dispatcher, callSubscriber} from './dispatcher' import {findByPred} from './utils/collections' function Observable() { this._dispatcher = new Dispatcher() this._active = false this._alive = true this._activating = false this._logHandlers = null this._spyHandlers = null } extend(Observable.prototype, { _name: 'observable', _onActivation() {}, _onDeactivation() {}, _setActive(active) { if (this._active !== active) { this._active = active if (active) { this._activating = true this._onActivation() this._activating = false } else { this._onDeactivation() } } }, _clear() { this._setActive(false) this._dispatcher.cleanup() this._dispatcher = null this._logHandlers = null }, _emit(type, x) { switch (type) { case VALUE: return this._emitValue(x) case ERROR: return this._emitError(x) case END: return this._emitEnd() } }, _emitValue(value) { if (this._alive) { this._dispatcher.dispatch({type: VALUE, value}) } }, _emitError(value) { if (this._alive) { this._dispatcher.dispatch({type: ERROR, value}) } }, _emitEnd() { if (this._alive) { this._alive = false this._dispatcher.dispatch({type: END}) this._clear() } }, _on(type, fn) { if (this._alive) { this._dispatcher.add(type, fn) this._setActive(true) } else { callSubscriber(type, fn, {type: END}) } return this }, _off(type, fn) { if (this._alive) { let count = this._dispatcher.remove(type, fn) if (count === 0) { this._setActive(false) } } return this }, onValue(fn) { return this._on(VALUE, fn) }, onError(fn) { return this._on(ERROR, fn) }, onEnd(fn) { return this._on(END, fn) }, onAny(fn) { return this._on(ANY, fn) }, offValue(fn) { return this._off(VALUE, fn) }, offError(fn) { return this._off(ERROR, fn) }, offEnd(fn) { return this._off(END, fn) }, offAny(fn) { return this._off(ANY, fn) }, observe(observerOrOnValue, onError, onEnd) { const _this = this let closed = false const observer = !observerOrOnValue || typeof observerOrOnValue === 'function' ? {value: observerOrOnValue, error: onError, end: onEnd} : observerOrOnValue const handler = function(event) { if (event.type === END) { closed = true } if (event.type === VALUE && observer.value) { observer.value(event.value) } else if (event.type === ERROR && observer.error) { observer.error(event.value) } else if (event.type === END && observer.end) { observer.end(event.value) } } this.onAny(handler) return { unsubscribe() { if (!closed) { _this.offAny(handler) closed = true } }, get closed() { return closed }, } }, // A and B must be subclasses of Stream and Property (order doesn't matter) _ofSameType(A, B) { return A.prototype.getType() === this.getType() ? A : B }, setName(sourceObs /* optional */, selfName) { this._name = selfName ? `${sourceObs._name}.${selfName}` : sourceObs return this }, log(name = this.toString()) { let isCurrent let handler = function(event) { let type = `<${event.type}${isCurrent ? ':current' : ''}>` if (event.type === END) { console.log(name, type) } else { console.log(name, type, event.value) } } if (this._alive) { if (!this._logHandlers) { this._logHandlers = [] } this._logHandlers.push({name: name, handler: handler}) } isCurrent = true this.onAny(handler) isCurrent = false return this }, offLog(name = this.toString()) { if (this._logHandlers) { let handlerIndex = findByPred(this._logHandlers, obj => obj.name === name) if (handlerIndex !== -1) { this.offAny(this._logHandlers[handlerIndex].handler) this._logHandlers.splice(handlerIndex, 1) } } return this }, spy(name = this.toString()) { let handler = function(event) { let type = `<${event.type}>` if (event.type === END) { console.log(name, type) } else { console.log(name, type, event.value) } } if (this._alive) { if (!this._spyHandlers) { this._spyHandlers = [] } this._spyHandlers.push({name: name, handler: handler}) this._dispatcher.addSpy(handler) } return this }, offSpy(name = this.toString()) { if (this._spyHandlers) { let handlerIndex = findByPred(this._spyHandlers, obj => obj.name === name) if (handlerIndex !== -1) { this._dispatcher.removeSpy(this._spyHandlers[handlerIndex].handler) this._spyHandlers.splice(handlerIndex, 1) } } return this }, }) // extend() can't handle `toString` in IE8 Observable.prototype.toString = function() { return `[${this._name}]` } export default Observable