[英]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.