簡體   English   中英

Angular Observable 需要先完成

[英]Angular Observable need to complete first

@Input() list: string[];

ngOnInit() : void {
   this.valueMap = new Map<any, any>();
   this.getDataFromService();
   this.buildContainer();
}

private getDataFromService(): void {
  this.list.forEach(value-> {
      this.fetchService(value).subscribe(data ->{
          this.valueMap.set(value,data);
          }
       )
   })
}

private buildContainer(): void {
   console.log(this.valueMap.size); // shows always 0 even when service returns the data 
 }

現在的事情是我必須在方法buidlContainer()中使用這個valueMap ,因此我需要首先從服務中獲取完整的 map。 當我在 buildConatainer() 中使用 map 時,它顯示我未定義。

我知道這是一些異步調用問題。 此外,我不能為 in.subscribe() 中的每個值調用方法 buildContainer(),因為這不是更好的主意。 所以,在進一步處理之前,我必須先計算 Map。

感謝您提供任何幫助,我無法將服務從返回 Observable 修改為返回 Promise

我想做如下

private buildContainer(): void {
   for([key,data] of this.valueMap) {
      -----some code----
    }
 }

只需使用forkJoin等待所有可觀察對象完成。 forkJoin將把所有的 observable 打包成一個。 這個 observable 可以返回並在ngOnInit訂閱它並調用buildContainer

 @Input() list: string[];

  ngOnInit(): void {
    this.valueMap = new Map<any, any>();
    this.getDataFromService().subscribe(_ => this.buildContainer());
  }

  private getDataFromService(): Observable<any> {
    return forkJoin(
      this.list.map(value =>
        this.fetchService(value).pipe(
          tap(data => this.valueMap.set(value, data))
        )
      )
    );
  }

  private buildContainer(): void {
    console.log(this.valueMap.size);
  }

如果您不在tap運算符中填充valueMap ,而是使用您在訂閱 function 中獲得的結果,代碼會更加簡潔。

這是將響應式范式引入代碼的缺點之一(或優點取決於您如何看待它)。 反應過程的單點會逐漸部分或完全潛入代碼的其他部分。

因此,我建議您也使valueMap具有響應性,並使其根據getDataFromService()服務中的更改進行響應。 一種方法是將valueMap變量設置為 RxJS ReplaySubject ,緩沖區為 1。因此它將緩沖(或保持)它的最后一個值,並在訂閱時立即發出它。

嘗試以下

import { forkJoin, ReplaySubject, Subject } from 'rxjs';
import { take, takeUntil } from 'rxjs/operators';

valueMap: ReplaySubject<Map<any, any>> = ReplaySubject<Map<any, any>>(1);
completed$ = new Subject<any>();

@Input() list: string[];

ngOnInit(): void {
  this.valueMap = new Map <any, any>();
  this.getDataFromService();
  this.buildContainer();
}

private getDataFromService(): void {
  forkJoin(this.list.map(value => this.fetchService(value))).pipe(
    takeUntil(this.completed$)             // <-- close subscription upon component `ngOnDestroy` hook trigger
  ).subscribe(
    data => {
      let map = new Map<any, any>();
      data.forEach((item, i) => {
        map.set(this.list[i], item);
      });
      this.valueMap.next(map);
    }
  );
}

private buildContainer(): void {
  this.valueMap.asObservable().pipe(
    take(1)               // <-- emit only the first notification and complete
  ).subscribe(
    data => {
      console.log(this.valueMap.size);
    }
  );
}

ngOnDestroy() {
  this.completed$.next();
  this.completed$.complete();
}

我還使用 RxJS forkJoin() function 來組合多個可觀察對象,並在組件被銷毀時使用takeUntil運算符關閉訂閱。

工作示例: Stackblitz

在示例中,我使用jsonplaceholder API 來模擬fetchService() function 中的一些 HTTP 調用。

筆記:

  1. 僅當所有可觀察對象都完成時, forkJoin()才會發出。 如果您正在處理 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ 數據,則必須根據您的要求將其替換為combineLatestzip

  2. 有一個明顯的問題,如果buildContainer() function 被快速連續調用多次,每個都會觸發單獨的訂閱。

ngOnInit 中刪除 buildContainer調用subscribe中的方法

 private getDataFromService(): void {
    this.list.forEach((value, index) -> {
        this.fetchService(value).subscribe(data ->{
            this.valueMap.set(value,data);
                   }
         )
         if(this.list.length-1 == index){
          this.buildContainer(); // call here
         }
     })
  }

暫無
暫無

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

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