看下面這段程式碼:
getSupportedDeliveryModes(): Observable<DeliveryMode[]> {
return this.checkoutStore.pipe(
select(CheckoutSelectors.getSupportedDeliveryModes),
withLatestFrom(
this.checkoutStore.pipe(
select(getProcessStateFactory(SET_SUPPORTED_DELIVERY_MODE_PROCESS_ID))
)
),
tap(([, loadingState]) => {
if (
!(loadingState.loading || loadingState.success || loadingState.error)
) {
this.loadSupportedDeliveryModes();
}
}),
pluck(0),
shareReplay({ bufferSize: 1, refCount: true })
);
}
呼叫 withLatestFrom 的 Observable 物件,起到主導資料產生給下游觀察者的作用。作為引數被呼叫的 Observable 物件只能貢獻新的資料,而不能控制資料的產生時機。
換句話說,上述 Spartacus 的例子,CheckoutSelectors.getSupportedDeliveryModes Observable 物件是向下遊產生資料的主導者,而 select(getProcessStateFactory(SET_SUPPORTED_DELIVERY_MODE_PROCESS_ID 只是資料片段的貢獻者。
下圖第 54 行的語法是元祖,元祖也是陣列,但各個元素的資料型別不一定必須相同。
第 54 行的 loadingState,代表的就是從 ngrx store 裡取出的 setDeliveryModeProcess 的狀態。第 55 行的語義是,如果狀態是 loading 或者 成功,或者是 error ,則不做任何事情,否則呼叫 58 行的 loadSupportedDeliveryModes, 進行 mode 的載入。
這裡我們巧妙的使用了 withLatestFrom, 將 delivery mode 的載入狀態,引入到 getSupportedDeliveryModes 的互動之中。
再看另一個例子:
protected buildRestoreSavedCartEvents<T>(
mapping: ActionToEventMapping<T>
): () => void {
const eventStream$ = this.getAction(mapping.action).pipe(
switchMap((action) =>
of(action).pipe(
withLatestFrom(this.multiCartService.getCart(action.payload.cartId))
)
),
map(([action, cart]) =>
createFrom(mapping.event as Type<T>, {
...action.payload,
cartCode: cart.code,
saveCartName: cart.name,
saveCartDescription: cart.description,
...(cart.saveTime && { saveTime: cart.saveTime }),
})
)
);
這裡呼叫 withLatestFrom 的 Observable 物件的型別為 Action,包含一個型別為 string 的欄位 type 和型別為 any 的 payload 欄位。
貢獻資料即傳入 withLatestFrom 操作符函式的輸入引數,型別為 146 行 this.multiCartService.getCart 的返回引數,型別為 Cart,如下圖所示:
因此,在 pipe 操作符下游即 149 行程式碼裡,map 的輸入引數為元祖:[ action, cart], 兩個元素的資料型別分別為:
以及:
值得一說的是,combineLatest 和 withLatestFrom 有本質的區別。在前者的呼叫裡,所有參與運算的 Observable 地位都是均等的,只要有任意一個發生變化,combineLatest 都會從所有的輸入 Observable 物件中拿出最後一次產生的資料,組合成陣列的資料型別,傳遞給下游。
更多Jerry的原創文章,盡在:"汪子熙":