RxJs 操作符 withLatestFrom 在 SAP 電商雲 Spartacus UI 中的應用

JerryWang_汪子熙發表於2022-02-15

看下面這段程式碼:

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的原創文章,盡在:"汪子熙":

相關文章