UNPKG

0xweb

Version:

Contract package manager and other web3 tools

138 lines (125 loc) 4.16 kB
import { Subscription } from "./Subscription"; import { SubjectKind } from "./SubjectKind"; import { class_Dfr, class_EventEmitter } from 'atma-utils'; export class SubjectStream<T = any> { public value: T = void 0; protected _error: Error | any = void 0; protected _inner: SubjectStream; protected _innerSub: Subscription; protected _pipe: SubjectStream; protected _pipeSub: Subscription protected _mapper: (value: any) => T; protected _events: class_EventEmitter; /// [SuccessCb, ErrorCb, Options][] protected _cbs: [((x: T) => void), ((err: Error | any) => void), { once?: boolean; }][] = []; public onConnected = new class_Dfr(); public kind = SubjectKind.Stream; public canceled: boolean = false; constructor() { this.next = this.next.bind(this); this.error = this.error.bind(this); this.onInnerChanged = this.onInnerChanged.bind(this); } next(x: T) { this.onValue(x) } onValue (val) { val = this._mapper?.(val) ?? val; this._error = void 0; this.value = val; this.call(0, val); this._events?.emit('data', val); } error(err: Error | any) { this._error = err; this.call(1, err); this._events?.emit('error', err); } connected (error?) { if (error != null) { this.onConnected.reject(error); this.error(error); return; } this.onConnected.resolve(); this._events?.emit('connected'); } current(): T { return this.value; } isBusy() { return this.value === void 0; } fromStream(stream: SubjectStream, inner?: SubjectStream) { this._pipe = stream; this._inner = inner; if (this._cbs.length !== 0) { this._pipeSub = stream.subscribe(this.next, this.error); } if (this.value === void 0 && stream.value != null) { this.value = stream.value; } this._innerSub = this._inner?.subscribe(this.onInnerChanged) } subscribe(cb: (x: T) => void, onError?: (x: Error | any) => void, once?): Subscription { if (this._pipe != null && this._cbs.length === 0) { this._pipe.subscribe(this.next, this.error); } this._cbs.push([cb, onError, once === true ? CB_ONCE : null]); if (this.value !== void 0) { this.onValue(this.value); } return new Subscription(this, cb); } unsubscribe(cb?: Function) { for (let i = 0; i < this._cbs.length; i++) { if (cb == null || this._cbs[i][0] === cb) { this._cbs.splice(i, 1); } } if (this._pipe != null && this._cbs.length === 0) { this._pipe.unsubscribe?.(this.next); this._innerSub?.unsubscribe?.(this.onInnerChanged); return; } } on (type: 'data' | 'connected' | 'error', cb) { this._events ??= new class_EventEmitter(); this._events.on(type, cb); } // When binding the to expression like: 'foo.bar.quxStream()' we create additional stream to listen to `foo.bar` properties reassignment private onInnerChanged (newStream) { this._pipe?.unsubscribe?.(this.next); this._pipe = newStream; if (this._pipe != null && this._cbs.length > 0) { this._pipe.subscribe(this.next, this.error); } if (newStream.value !== void 0) { this.next(newStream.value); } } private call(index: CallbackType, x: any) { for (let i = 0; i < this._cbs.length; i++) { let row = this._cbs[i]; let fn = row[index]; let opts = row[2]; if (opts?.once === true) { this._cbs.splice(i, 1); } if (fn == null) { if (index === 1) { console.info(`Error not handled`, x); } return; } fn(x); } } } enum CallbackType { OK = 0, Error = 1 } const CB_ONCE = { once: true };