-
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(exhaust): add higher-order lettable version of exhaust
- Loading branch information
Showing
4 changed files
with
95 additions
and
142 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
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,88 @@ | ||
import { Operator } from '../Operator'; | ||
import { Observable } from '../Observable'; | ||
import { Subscriber } from '../Subscriber'; | ||
import { Subscription, TeardownLogic } from '../Subscription'; | ||
import { OuterSubscriber } from '../OuterSubscriber'; | ||
import { subscribeToResult } from '../util/subscribeToResult'; | ||
import { MonoTypeOperatorFunction } from '../interfaces'; | ||
|
||
/** | ||
* Converts a higher-order Observable into a first-order Observable by dropping | ||
* inner Observables while the previous inner Observable has not yet completed. | ||
* | ||
* <span class="informal">Flattens an Observable-of-Observables by dropping the | ||
* next inner Observables while the current inner is still executing.</span> | ||
* | ||
* <img src="./img/exhaust.png" width="100%"> | ||
* | ||
* `exhaust` subscribes to an Observable that emits Observables, also known as a | ||
* higher-order Observable. Each time it observes one of these emitted inner | ||
* Observables, the output Observable begins emitting the items emitted by that | ||
* inner Observable. So far, it behaves like {@link mergeAll}. However, | ||
* `exhaust` ignores every new inner Observable if the previous Observable has | ||
* not yet completed. Once that one completes, it will accept and flatten the | ||
* next inner Observable and repeat this process. | ||
* | ||
* @example <caption>Run a finite timer for each click, only if there is no currently active timer</caption> | ||
* var clicks = Rx.Observable.fromEvent(document, 'click'); | ||
* var higherOrder = clicks.map((ev) => Rx.Observable.interval(1000).take(5)); | ||
* var result = higherOrder.exhaust(); | ||
* result.subscribe(x => console.log(x)); | ||
* | ||
* @see {@link combineAll} | ||
* @see {@link concatAll} | ||
* @see {@link switch} | ||
* @see {@link mergeAll} | ||
* @see {@link exhaustMap} | ||
* @see {@link zipAll} | ||
* | ||
* @return {Observable} An Observable that takes a source of Observables and propagates the first observable | ||
* exclusively until it completes before subscribing to the next. | ||
* @method exhaust | ||
* @owner Observable | ||
*/ | ||
export function exhaust<T>(): MonoTypeOperatorFunction<T> { | ||
return (source: Observable<T>) => source.lift(new SwitchFirstOperator<T>()); | ||
} | ||
|
||
class SwitchFirstOperator<T> implements Operator<T, T> { | ||
call(subscriber: Subscriber<T>, source: any): TeardownLogic { | ||
return source.subscribe(new SwitchFirstSubscriber(subscriber)); | ||
} | ||
} | ||
|
||
/** | ||
* We need this JSDoc comment for affecting ESDoc. | ||
* @ignore | ||
* @extends {Ignored} | ||
*/ | ||
class SwitchFirstSubscriber<T> extends OuterSubscriber<T, T> { | ||
private hasCompleted: boolean = false; | ||
private hasSubscription: boolean = false; | ||
|
||
constructor(destination: Subscriber<T>) { | ||
super(destination); | ||
} | ||
|
||
protected _next(value: T): void { | ||
if (!this.hasSubscription) { | ||
this.hasSubscription = true; | ||
this.add(subscribeToResult(this, value)); | ||
} | ||
} | ||
|
||
protected _complete(): void { | ||
this.hasCompleted = true; | ||
if (!this.hasSubscription) { | ||
this.destination.complete(); | ||
} | ||
} | ||
|
||
notifyComplete(innerSub: Subscription): void { | ||
this.remove(innerSub); | ||
this.hasSubscription = false; | ||
if (this.hasCompleted) { | ||
this.destination.complete(); | ||
} | ||
} | ||
} |
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