[英]Do parts of an rxjs pipe only for certain emissions of the Observable
I have an rxjs-Observable which I subscribe to.我有一个我订阅的 rxjs-Observable。 I now have two different necessities to handle within the pipe of the Observable.我现在有两种不同的需要在 Observable 的管道内处理。
First I want to map the output.首先我想映射输出。 Second I want to use tap
to trigger a side-effect, but the side-effect must not be triggered on the first emission.其次,我想使用tap
来触发副作用,但不能在第一次发射时触发副作用。
So this obviously does not work, because the skip
is working globally on the pipe:所以这显然不起作用,因为skip
在管道上全局工作:
this.userChangeSubscription = this.userStateService.userState$
.pipe(
map(userState => userState.prop),
skip(1),
tap(() => this.sideEffect())
)
.subscribe();
Is there any way to do this, without subscribing to the Observable twice?有没有办法做到这一点,而无需订阅 Observable 两次?
edit : Ok, I now have several options that all seem to be working.编辑:好的,我现在有几个似乎都有效的选项。 Now: which one to choose?现在:选择哪一个?
I've seen that there are already working answers, but I think your best bet would be to write your own rxjs operator for this usecase.我已经看到已经有有效的答案,但我认为最好的办法是为此用例编写自己的 rxjs 运算符。 This results in a clean solution and inside your pipe
function clarifies your intent.这会产生一个干净的解决方案,并且在您的pipe
函数中阐明了您的意图。
To do so we need to make use of thedefer
observable :为此,我们需要使用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;
})
);
});
};
}
We are using the skip
variable to decide whether we run the side effect function or not.我们使用skip
变量来决定是否运行副作用函数。 At the end of the first run we toggle skip
to be false and therefore run the side effect function for all following runs.在第一次运行结束时,我们将skip
切换为 false,因此为所有后续运行运行副作用函数。 We need to use the defer
observable here, because the code inside defer
only gets called on subscription and not on creation.我们需要在这里使用defer
observable,因为defer
的代码只在订阅时调用,而不是在创建时调用。 This is important because otherwise all subscriptions would share the same skip
variable.这很重要,否则所有订阅都将共享相同的skip
变量。
And now you can easily use the new custom operator like:现在您可以轻松使用新的自定义运算符,例如:
this.userChangeSubscription = this.userStateService.userState$
.pipe(
map(userState => userState.prop),
tapSkipFirst(() => this.sideEffect())
)
.subscribe();
Yes it is possible.对的,这是可能的。 Example:例子:
const userProp$ = this.userStateService.userState$.pipe(
map(userState => userState.prop),
shareReplay({ bufferSize: 1, refCount: true })
);
Here shareReplay will let you share that resource between multiple subscribers without triggering the whole chain again.在这里 shareReplay 将让您在多个订阅者之间共享该资源,而无需再次触发整个链。 Once all the subscribers are done listening, the shareReplay will end and the observable above will be ended.一旦所有订阅者都收听完毕,shareReplay 将结束,上面的 observable 也将结束。
Then you can subscribe as many times as you want and also isolate the side effect which is a good idea in the first place:然后,您可以根据需要多次订阅,并隔离副作用,这首先是一个好主意:
userProp$
.pipe(
skip(1),
tap(() => this.sideEffect())
)
.subscribe();
You can use the switchMap
operator for such use case.您可以将switchMap
运算符用于此类用例。 It gives you an index
parameter allowing you to perform different actions based on the emission being the first, second, third etc..它为您提供了一个index
参数,允许您根据第一个、第二个、第三个等发射来执行不同的操作。
In this case, the sideEffect
will not get triggered if the index
equals 0 (the first emission) but will get triggered for any other futures emissions.在这种情况下,如果index
等于 0(第一次排放),则sideEffect
不会被触发,但会因任何其他期货排放而被触发。
I use this operator for triggering side effects or altering the value being emitted based on a condition.我使用此运算符来触发副作用或根据条件更改发出的值。 This is great because you don't need to split the source Observable into two different Observables having a different pipeline.这很棒,因为您不需要将源 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.