繁体   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