[英]How to do initialization logic on an observable pipe once per subscription in rxjs
[英]Do parts of an rxjs pipe only for certain emissions of the Observable
我有一个我订阅的 rxjs-Observable。 我现在有两种不同的需要在 Observable 的管道内处理。
首先我想映射输出。 其次,我想使用tap
来触发副作用,但不能在第一次发射时触发副作用。
所以这显然不起作用,因为skip
在管道上全局工作:
this.userChangeSubscription = this.userStateService.userState$
.pipe(
map(userState => userState.prop),
skip(1),
tap(() => this.sideEffect())
)
.subscribe();
有没有办法做到这一点,而无需订阅 Observable 两次?
编辑:好的,我现在有几个似乎都有效的选项。 现在:选择哪一个?
我已经看到已经有有效的答案,但我认为最好的办法是为此用例编写自己的 rxjs 运算符。 这会产生一个干净的解决方案,并且在您的pipe
函数中阐明了您的意图。
为此,我们需要使用defer
observable :
function tapSkipFirst<T>(fn: Function): OperatorFunction<T, T> {
return function(source: Observable<any>) {
return defer(() => {
let skip = true;
return source.pipe(
tap((v?:any) => {
if (!skip) {
fn(v);
}
skip = false;
})
);
});
};
}
我们使用skip
变量来决定是否运行副作用函数。 在第一次运行结束时,我们将skip
切换为 false,因此为所有后续运行运行副作用函数。 我们需要在这里使用defer
observable,因为defer
的代码只在订阅时调用,而不是在创建时调用。 这很重要,否则所有订阅都将共享相同的skip
变量。
现在您可以轻松使用新的自定义运算符,例如:
this.userChangeSubscription = this.userStateService.userState$
.pipe(
map(userState => userState.prop),
tapSkipFirst(() => this.sideEffect())
)
.subscribe();
对的,这是可能的。 例子:
const userProp$ = this.userStateService.userState$.pipe(
map(userState => userState.prop),
shareReplay({ bufferSize: 1, refCount: true })
);
在这里 shareReplay 将让您在多个订阅者之间共享该资源,而无需再次触发整个链。 一旦所有订阅者都收听完毕,shareReplay 将结束,上面的 observable 也将结束。
然后,您可以根据需要多次订阅,并隔离副作用,这首先是一个好主意:
userProp$
.pipe(
skip(1),
tap(() => this.sideEffect())
)
.subscribe();
您可以将switchMap
运算符用于此类用例。 它为您提供了一个index
参数,允许您根据第一个、第二个、第三个等发射来执行不同的操作。
在这种情况下,如果index
等于 0(第一次排放),则sideEffect
不会被触发,但会因任何其他期货排放而被触发。
我使用此运算符来触发副作用或根据条件更改发出的值。 这很棒,因为您不需要将源 Observable 拆分为具有不同管道的两个不同 Observable。
this.userChangeSubscription = this.userStateService.userState$
.pipe(
map(userState => userState.prop),
switchMap((value, index) => {
return index > 0
? of(value).pipe(tap(() => this.sideEffect()))
: of(value);
})
)
.subscribe();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.