-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(bufferWhen): add higher-order lettable version of bufferWhen
- Loading branch information
Showing
3 changed files
with
140 additions
and
97 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { Operator } from '../Operator'; | ||
import { Subscriber } from '../Subscriber'; | ||
import { Observable } from '../Observable'; | ||
import { Subscription } from '../Subscription'; | ||
import { tryCatch } from '../util/tryCatch'; | ||
import { errorObject } from '../util/errorObject'; | ||
import { OuterSubscriber } from '../OuterSubscriber'; | ||
import { InnerSubscriber } from '../InnerSubscriber'; | ||
import { subscribeToResult } from '../util/subscribeToResult'; | ||
import { OperatorFunction } from '../interfaces'; | ||
|
||
/** | ||
* Buffers the source Observable values, using a factory function of closing | ||
* Observables to determine when to close, emit, and reset the buffer. | ||
* | ||
* <span class="informal">Collects values from the past as an array. When it | ||
* starts collecting values, it calls a function that returns an Observable that | ||
* tells when to close the buffer and restart collecting.</span> | ||
* | ||
* <img src="./img/bufferWhen.png" width="100%"> | ||
* | ||
* Opens a buffer immediately, then closes the buffer when the observable | ||
* returned by calling `closingSelector` function emits a value. When it closes | ||
* the buffer, it immediately opens a new buffer and repeats the process. | ||
* | ||
* @example <caption>Emit an array of the last clicks every [1-5] random seconds</caption> | ||
* var clicks = Rx.Observable.fromEvent(document, 'click'); | ||
* var buffered = clicks.bufferWhen(() => | ||
* Rx.Observable.interval(1000 + Math.random() * 4000) | ||
* ); | ||
* buffered.subscribe(x => console.log(x)); | ||
* | ||
* @see {@link buffer} | ||
* @see {@link bufferCount} | ||
* @see {@link bufferTime} | ||
* @see {@link bufferToggle} | ||
* @see {@link windowWhen} | ||
* | ||
* @param {function(): Observable} closingSelector A function that takes no | ||
* arguments and returns an Observable that signals buffer closure. | ||
* @return {Observable<T[]>} An observable of arrays of buffered values. | ||
* @method bufferWhen | ||
* @owner Observable | ||
*/ | ||
export function bufferWhen<T>(closingSelector: () => Observable<any>): OperatorFunction<T, T[]> { | ||
return function (source: Observable<T>) { | ||
return source.lift(new BufferWhenOperator(closingSelector)); | ||
}; | ||
} | ||
|
||
class BufferWhenOperator<T> implements Operator<T, T[]> { | ||
|
||
constructor(private closingSelector: () => Observable<any>) { | ||
} | ||
|
||
call(subscriber: Subscriber<T[]>, source: any): any { | ||
return source.subscribe(new BufferWhenSubscriber(subscriber, this.closingSelector)); | ||
} | ||
} | ||
|
||
/** | ||
* We need this JSDoc comment for affecting ESDoc. | ||
* @ignore | ||
* @extends {Ignored} | ||
*/ | ||
class BufferWhenSubscriber<T> extends OuterSubscriber<T, any> { | ||
private buffer: T[]; | ||
private subscribing: boolean = false; | ||
private closingSubscription: Subscription; | ||
|
||
constructor(destination: Subscriber<T[]>, private closingSelector: () => Observable<any>) { | ||
super(destination); | ||
this.openBuffer(); | ||
} | ||
|
||
protected _next(value: T) { | ||
this.buffer.push(value); | ||
} | ||
|
||
protected _complete() { | ||
const buffer = this.buffer; | ||
if (buffer) { | ||
this.destination.next(buffer); | ||
} | ||
super._complete(); | ||
} | ||
|
||
protected _unsubscribe() { | ||
this.buffer = null; | ||
this.subscribing = false; | ||
} | ||
|
||
notifyNext(outerValue: T, innerValue: any, | ||
outerIndex: number, innerIndex: number, | ||
innerSub: InnerSubscriber<T, any>): void { | ||
this.openBuffer(); | ||
} | ||
|
||
notifyComplete(): void { | ||
if (this.subscribing) { | ||
this.complete(); | ||
} else { | ||
this.openBuffer(); | ||
} | ||
} | ||
|
||
openBuffer() { | ||
|
||
let { closingSubscription } = this; | ||
|
||
if (closingSubscription) { | ||
this.remove(closingSubscription); | ||
closingSubscription.unsubscribe(); | ||
} | ||
|
||
const buffer = this.buffer; | ||
if (this.buffer) { | ||
this.destination.next(buffer); | ||
} | ||
|
||
this.buffer = []; | ||
|
||
const closingNotifier = tryCatch(this.closingSelector)(); | ||
|
||
if (closingNotifier === errorObject) { | ||
this.error(errorObject.e); | ||
} else { | ||
closingSubscription = new Subscription(); | ||
this.closingSubscription = closingSubscription; | ||
this.add(closingSubscription); | ||
this.subscribing = true; | ||
closingSubscription.add(subscribeToResult(this, closingNotifier)); | ||
this.subscribing = false; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters