簡體   English   中英

帶有 ngrx 存儲的 Angular Rxjs 可觀察鏈

[英]Angular Rxjs Observable Chain with ngrx store

我在 ngrx 商店中使用 Angular 7。 商店包含應用程序狀態,我在 OnInit 上的應用程序組件中訂閱它。 商店中有幾個變量,它們是可以互換的(用按鈕交換)。

這是我在組件中的示例代碼。

this.appState.getGasStationSushi().pipe(switchMap((sushi) => {
  this.gsSushi = sushi;
  return this.appState.getStarbucksSushi();
}), switchMap((sushi) => {
  this.sbSushi = sushi;
  return this.service.compare(this.gsSushi, this.sbSushi);
}).subscribe((result)=>{
  console.log(result);
}));

通過在視圖中單擊按鈕,用戶可以更改兩個sushi值,這會導致最后一次訂閱調用兩次,這是有道理的(RxJS)。 我可以刪除switchMap並寫一些類似的東西

-- gas station sushi susbcription 
   -- star bucks sushi susbcription 
      -- compare

我不是這個的忠實粉絲,我相信一定有一個rxjs/operator 有人可以提出建議嗎?

此外,試圖forkjoin ,但NGRX店好像,一個需要調用firstlast類似下面。 這里是對上面語句forkjoinWithstore的參考

const $sushiObs = [
  this.appState.getGasStationSushi().pipe(first()),
  this.appState.getStarbucksSushi().pipe(first())
];
forkjoin($sushiObs).subscribe((result) => {
  console.log(result);
});

如果我使用上述模式,訂閱會第一次觸發,但之后根本不會觸發。

首先,這是一個關於 stackblitz 的工作示例

我沒有使用商店,而是創建了一個返回 observable 的模擬類SushiState

class SushiState {
  private _gas = new BehaviorSubject(1);
  private _starbucks = new BehaviorSubject(1);

  public get gas() {
    return this._gas.asObservable();
  }
  public get starbucks() {
    return this._gas.asObservable();
  }

  public increaseSushi(n = 1) {
    this._gas.next(this._gas.value + n);
    this._starbucks.next(this._starbucks.value + n);
  }

  public static compareSushi(g: number, s: number): string {
    return `gas is ${g}, starbucks is ${s}`;
  }
}

至於組件,這里是代碼。

export class AppComponent implements OnInit {
  state: SushiState;

  gasSushi: Observable<number>;
  sbSushi: Observable<number>;

  combined: string;
  combinedTimes = 0;
  zipped: string;
  zippedTimes = 0;

  ngOnInit() {
    this.state = new SushiState;
    this.gasSushi = this.state.gas;
    this.sbSushi = this.state.gas;

    const combined = combineLatest(
      this.gasSushi,
      this.sbSushi,
    ).pipe(
      tap((sushi) => {
        console.log('combined', sushi);
        this.combinedTimes++;
      }),
      map((sushi) => SushiState.compareSushi(sushi[0], sushi[1])),
    );
    combined.subscribe(result => this.combined = result);

    const zipped = zip(
      this.gasSushi,
      this.sbSushi,
    ).pipe(
      tap((sushi) => {
        console.log('zipped', sushi);
        this.zippedTimes++;
      }),
      map((sushi) => SushiState.compareSushi(sushi[0], sushi[1])),
    );
    zipped.subscribe(result => this.zipped = result);
  }

  increaseSushi() {
    this.state.increaseSushi();
  }

}

如果您在 stackblitz 上運行它並檢查控制台,您將看到以下行為:

控制台輸出

如果我們使用combined latest ,我們將observables 分開組合,只關心最新的狀態,導致console.log 2 次調用。

我們可以改為使用zip ,它會等待兩個 observable 發出,然后再產生輸出。 這看起來非常適合我們的“Increase Both”按鈕,但有一個問題:如果starbucksSushi單獨增加(可能來自應用程序的不同部分), zipped版本將等待加油站壽司也更新.

要建議第三種解決方案,你可以使用combineLatest的壽司櫃台合並,然后用debounceTime運營商發射輸出之前等待數毫秒。

const debounced = zip(
  this.gasSushi,
  this.sbSushi,
).pipe(
  tap((sushi) => {
    console.log('debounced', sushi);
    this.debouncedTimes++;
  }),
  map((sushi) => SushiState.compareSushi(sushi[0], sushi[1])),
  debounceTime(100),
);
debounced.subscribe(result => this.debounced = result);

這將對所有來源的變化做出反應,但不會超過100ms

最后,你必須做first()

forkJoin在完成后加入 observables(它只能發生一次,所以它不適合連續流)並且更適合“類似承諾”的工作,例如 HTTP 調用、進程完成等。順便說一句,如果你只從流中取出一個元素,結果流在單次發射后完成。

聚苯乙烯

我建議使用async管道來處理 observables(就像我對屬性所做的那樣

gasSushi: Observable<number>;
sbSushi: Observable<number>;

然后在模板內部

<div>
  <h3>starbucks sushi</h3>
  <p>{{sbSushi | async}}</p>
</div>

代替

result => this.zipped = result

我在這個例子中同時使用了兩者,所以你可以比較它們。 根據我的經驗,一旦您提前停止轉換“取消觀察”並讓async管道完成其工作,使用可觀察對象就會變得容易得多。

最重要的是,如果您在組件的某處使用subscribe ,您應該在組件被銷毀時unsubscribe - 這一點都不難,但如果我們從不顯式訂閱,並允許async管道進行訂閱,它也會處理對我們的破壞:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM