【问题标题】:Angular 7 wait for all subscriptionsAngular 7等待所有订阅
【发布时间】:2019-09-05 11:07:39
【问题描述】:

我有一个组件,目前是这样设置的:

export class CompareComponent implements OnInit, OnDestroy {
  criteria: Criteria[]
  products: Product[] = [];

  private organisationId: string

  private criteriaSubscription: Subscription
  private routeSubscription: Subscription
  private productSubscription: Subscription

  constructor(
    @Inject(PLATFORM_ID) private platformId: Object,
    private route: ActivatedRoute,

    private attributeMatchService: AttributeMatchService,
    private criteriaService: CriteriaService,
    private optionsService: OptionsService,
    private productService: ProductService,
  ) { }

  ngOnInit() {
    this.organisationId = this.optionsService.get().organisation;
    this.routeSubscription = this.route.paramMap.subscribe(paramMap => {
      let category = paramMap.get('category');
      let gtins = paramMap.get('gtins').split('.');
      this.listCriteria(category);
      this.listProducts(category, gtins);
    });
  }

  ngOnDestroy() {
    if (this.criteriaSubscription) this.criteriaSubscription.unsubscribe();
    if (this.productSubscription) this.productSubscription.unsubscribe();
    if (this.routeSubscription) this.routeSubscription.unsubscribe();
  }

  close() {
    if (isPlatformServer(this.platformId)) return;
    window.history.back();
  }

  private listCriteria(category: string): void {
    this.criteriaSubscription = this.criteriaService.list(category, 'Attributes').subscribe(criteria => this.criteria = criteria);
  }

  private listProducts(category: string, gtins: string[]) {
    gtins.forEach(gtin => {
      this.productSubscription = this.productService.get(category, this.organisationId, parseInt(gtin)).subscribe(products => {
        this.products.push(products[0]);
      });
    });
  }
}

listProducts 方法中可以看出,我得到了一个基于作为参数传递的gtins 的产品列表。 我想做的是等待所有订阅在listProducts 中完成,并在完成后运行一些代码。

我该怎么做?

【问题讨论】:

标签: angular promise


【解决方案1】:

我相信你需要使用forkJoin

它等待数组中的所有 Observables 完成,然后发出。

tap 中对它们每个进行单独的可观察操作。

private listProducts(category: string, gtins: string[]) {
    const products$: Observable<any>[] = [];

    gtins.forEach(gtin => {
        products$.push(this.productService.get(category, 1, parseInt(gtin))
                        .pipe(tap(product => {
                              this.products.push(products[0]);
        }));
    });

    forkJoin(products$).subscribe(() => {
        console.log('All subscriptions done');
    });
}

【讨论】:

    【解决方案2】:

    如果您想等待所有 observables,请使用 forkJoin 运算符,然后将结果展平以将其推送到您的 products 数组。

    我还建议使用 takeUntil 运算符作为订阅的清理助手。看看如何在我的示例中使用它。

    forkJoin(
          gtins.forEach(gtin => this.productService.get(category, this.organisationId, parseInt(gtin)))
        ).pipe(
          takeUntil(this.destroyed$)
        ).subscribe(products => {
          this.products.push(...products.flat())
        });
    

    在你的onDestroy钩子里

      onDestroy() {
        this.destroyed$.next();
        this.destroyed$.complete();
      }
    

    在您的组件中,将 destroyed$ 定义为

    destroyed$: Subject&lt;any&gt; = new Subject&lt;any&gt;();

    【讨论】:

    • @ritaj 击败了我,但我将把它作为链接答案的参考。
    • 推送subscribe而不是map不是更习惯吗? (AFAIK map 在函数式反应式编程中应该没有副作用)
    • @meriton,非常正确,因为我们不改变数据,所以不需要地图!谢谢! :)
    • @meriton 是的,应该在tapsubscribe 内完成。
    • @DanielB 不仅如此,您还会将结果映射到this.products 数组的长度(因为 push 返回数组长度),这将丢失您以后进行流链接的信息。
    猜你喜欢
    • 2021-04-14
    • 1970-01-01
    • 2019-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-06
    相关资源
    最近更新 更多