我需要一个类似于exahustMap
的运算符,但是该运算符会记住最后一个被跳过的可观察对象,并在当前可观察对象完成后执行它。
例如,考虑exhaustMap
的大理石图:
在我的情况下,发出蓝色值之后,将跟随三个值50。当然,在这种情况下,它看起来像concatMap
,但是如果在3和之间还有一个4 5,它不会反映在输出中。
我设法编写了自己的运算符,类似于实现exhaustMap
的方式:
function exhaustLatestMap<T, R>(project: (value: T) => Subscribable<R>): OperatorFunction<T, R> {
return source => new Observable<R>(observer =>
source.subscribe(new ExhaustLatestMapOperatorSubscriber(observer, project)));
}
class ExhaustLatestMapOperatorSubscriber<T, R> implements Observer<T> {
constructor(
private observer: Subscriber<R>,
private project: (value: T) => Subscribable<R>) { }
innerSub: AnonymousSubscription = null;
latestValue: T;
next(value: T) {
this.processNext(value);
}
error(err) {
this.observer.error(err);
}
complete() {
this.observer.complete();
}
private processNext(value: T) {
this.latestValue = value;
if (!this.innerSub) {
this.innerSub = this.project(value).subscribe({
next: v => this.observer.next(v),
error: err => {
this.observer.error(err);
this.endInnerSub(value)
},
complete: () => {
this.endInnerSub(value);
}
});
}
}
private endInnerSub(value: T) {
this.innerSub.unsubscribe();
this.innerSub = null;
if (this.latestValue !== value) {
this.processNext(this.latestValue);
}
}
}
但是我想知道是否有一种方法可以通过重用和合并现有的运算符来实现。有什么想法吗?
答案 0 :(得分:1)
可以仅使用内置工厂和操作员来实现它。但是,对于AFAICT,如果不管理某些按预订状态就无法完成。
幸运的是,defer
工厂功能使管理每个订阅状态变得相对简单和安全。并且,除了帮助管理按预订状态之外,defer
还可作为一种机制,用于在观察到可观察对象的订阅时间得到通知。
另一种实现方式:
const {
concat,
defer,
EMPTY,
merge,
of
} = rxjs;
const {
delay,
mergeMap,
tap
} = rxjs.operators;
const exhaustMapLatest = project => source => defer(() => {
let latestValue;
let hasLatestValue = false;
let isExhausting = false;
const next = value => defer(() => {
if (isExhausting) {
latestValue = value;
hasLatestValue = true;
return EMPTY;
}
hasLatestValue = false;
isExhausting = true;
return project(value).pipe(
tap({ complete: () => isExhausting = false }),
s => concat(s, defer(() => hasLatestValue ?
next(latestValue) :
EMPTY
))
);
});
return source.pipe(mergeMap(next));
});
const source = merge(
of(0).pipe(delay(0)),
of(1000).pipe(delay(1000)),
of(1100).pipe(delay(1100)),
of(1200).pipe(delay(1200)),
of(2000).pipe(delay(2000))
);
source.pipe(
exhaustMapLatest(value => merge(
of(`${value}:0`).pipe(delay(0)),
of(`${value}:150`).pipe(delay(150)),
of(`${value}:300`).pipe(delay(300))
))
).subscribe(value => console.log(value));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs@6/bundles/rxjs.umd.min.js"></script>
此实现与您的实现在行为上有一些区别:
hasLatestValue
标志而不是相等性检查,因此,如果最新值等于初始值,则仍会进行投影。我不主张应以这种方式实施。答案只是为了显示替代实施方式。