import { checkFunction, checkObservable, checkProperty } from "../_check" import { NONE, NOOP_SUBSCRIBER, NOOP_SUBSCRIPTION, Source, Subscriber, Subscription, } from "../_core" import { In, Out } from "../_interfaces" import { makeObservable } from "../_obs" import { Transaction } from "../_tx" import { curry2, curry3 } from "../_util" import { dispatcherOf, Observable } from "../Observable" import { Property } from "../Property" import { Pipe, PipeSubscriber } from "./_base" import { JoinOperator } from "./_join" export const sampleWith: CurriedSampleWith = curry3(_sampleWith) export interface CurriedSampleWith { ( sampler: In, project: (value: ValueType, sample: SampleType) => ResultType, value: Property, ): Out (sampler: In): ( project: (value: ValueType, sample: SampleType) => ResultType, value: Property, ) => Out ( sampler: In, project: (value: ValueType, sample: SampleType) => ResultType, ): (value: Property) => Out (sampler: In): ( project: (value: ValueType, sample: SampleType) => ResultType, ) => (value: Property) => Out } export const sampleBy: CurriedSampleBy = curry2(_sampleBy) export interface CurriedSampleBy { ( sampler: In, value: Property, ): Out (sampler: In): ( value: Property, ) => Out } function _sampleWith( sampler: Observable, project: (value: V, sample: S) => R, value: Property, ): Observable { return _sampleF(sampler, project, value) } function _sampleBy(sampler: Observable, value: Property): Observable { return _sampleF(sampler, (v: V, s: S) => v, value) } function _sampleF( sampler: Observable, project: (value: V, sample: S) => R, value: Property, ): Observable { checkObservable(sampler) checkFunction(project) checkProperty(value) return makeObservable(sampler, new Sample(dispatcherOf(value), dispatcherOf(sampler), project)) } class Sample extends JoinOperator implements PipeSubscriber { protected source!: SVSource private sample: S = NONE private val: V = NONE constructor(vSrc: Source, sSrc: Source, private p: (value: V, sample: S) => R) { super(new SVSource(vSrc, sSrc, NOOP_SUBSCRIBER)) this.source.vDest = new Pipe(this) } public dispose(): void { this.sample = this.val = NONE super.dispose() } public next(tx: Transaction, sample: S): void { this.sample = sample this.fork(tx) } public error(tx: Transaction, err: Error): void { this.forkError(tx, err) } public end(tx: Transaction): void { this.forkEnd(tx) } public pipedNext(sender: Pipe, tx: Transaction, val: V): void { this.val = val } public pipedError(sender: Pipe, tx: Transaction, err: Error): void { this.forkError(tx, err) } public pipedEnd(sender: Pipe, tx: Transaction): void { this.source.disposeValue() } public join(tx: Transaction): void { const { val, sample } = this if (val !== NONE && sample !== NONE) { const project = this.p const result = project(this.val, this.sample) this.sample = NONE this.sink.next(tx, result) } else { this.sample = NONE } super.join(tx) } } export class SVSource implements Source, Subscription { public readonly weight: number private vSubs: Subscription private sSubs: Subscription constructor(public vSrc: Source, public sSrc: Source, public vDest: Subscriber) { this.weight = vSrc.weight + sSrc.weight this.vSubs = this.sSubs = NOOP_SUBSCRIPTION } public subscribe(subscriber: Subscriber, order: number): Subscription { this.vSubs = this.vSrc.subscribe(this.vDest, order) this.sSubs = this.sSrc.subscribe(subscriber, order) return this } public activate(initialNeeded: boolean): void { this.vSubs.activate(initialNeeded) this.sSubs.activate(initialNeeded) } public reorder(order: number): void { this.vSubs.reorder(order) this.sSubs.reorder(order) } public dispose(): void { const { sSubs, vSubs } = this this.vSubs = this.sSubs = NOOP_SUBSCRIPTION sSubs.dispose() vSubs.dispose() } public disposeValue(): void { const { vSubs } = this this.vSubs = NOOP_SUBSCRIPTION vSubs.dispose() } }