import { checkNaturalInt } from "../_check" import { sendEndInTx, sendNextInTx, Source } from "../_core" import { In, Out } from "../_interfaces" import { makeObservable } from "../_obs" import { Transaction } from "../_tx" import { curry2 } from "../_util" import { dispatcherOf, Observable } from "../Observable" import { OnTimeout, scheduleTimeout, Timeout } from "../scheduler/index" import { InitialAndChanges } from "./_changes" export const delay: CurriedDelay = curry2(_delay) export interface CurriedDelay { (time: number, observable: In): Out (time: number): ( observable: In, ) => Out } function _delay(time: number, observable: Observable): Observable { checkNaturalInt(time) return makeObservable(observable, new Delay(dispatcherOf(observable), time)) } class Delay extends InitialAndChanges { private h: Delayed | null = null private t: Delayed | null = null constructor(source: Source, private readonly time: number) { super(source) } public dispose(): void { let head = this.h this.t = this.h = null while (head !== null) { head.to.cancel() head = head.t } super.dispose() } public nextInitial(tx: Transaction, val: T): void { this.sink.next(tx, val) } public nextChange(tx: Transaction, val: T): void { this.t !== null ? (this.t = this.t.t = new Delayed(this.time, this, val, this.t, null)) : (this.t = this.h = new Delayed(this.time, this, val, null, null)) } public end(tx: Transaction): void { if (this.t === null) { this.sink.end(tx) } else { this.t = this.t.t = new DelayedEnd(this.time, this, null, this.t, null) } } public delayedNext(d: Delayed) { d.h !== null ? (d.h.t = d.t) : (this.h = d.t) d.t !== null ? (d.t.h = d.h) : (this.t = d.h) sendNextInTx(this.sink, d.v) } public delayedEnd(d: Delayed) { d.h !== null ? (d.h.t = d.t) : (this.h = d.t) d.t !== null ? (d.t.h = d.h) : (this.t = d.h) sendEndInTx(this.sink) } } class Delayed implements OnTimeout { public readonly to: Timeout constructor( time: number, public readonly d: Delay, public readonly v: T, public h: Delayed | null, public t: Delayed | null, ) { this.to = scheduleTimeout(this, time) } public due(): void { this.d.delayedNext(this) } } class DelayedEnd extends Delayed { public due(): void { this.d.delayedEnd(this) } }