【问题标题】:Angular 9 Subscribe inside subscribeAngular 9订阅内订阅
【发布时间】:2020-10-11 10:55:50
【问题描述】:

我正在使用 Angular 9 和 rxjs 6,我正在尝试获取节点列表:

NodeService.ts

public getList(): Observable<Nodes[]> { return this.http.get<Nodes[]>(URL); }

节点实体:

export class Nodes {
     public id: number;
     public name: string;
     public site_id: number;
     public site: Site;
}

nodes.component.ts:

public getAllNodes() {
  this.dataSource.data = [];
  this.nodeService.getList()
    .subscribe(
      nodes => {
        nodes.map(
          (node) => {
            this.siteService.getSiteById(node.site_id).subscribe(site => {
              node.site = site;
            })
          });
        this.dataSource.data = nodes;
        console.log(nodes);
      },
      error => {
        console.log(error);
      },
      () => {
      }
    );
}

=> 默认情况下,node.site = null,所以我使用 node.site_id 在 subscribe 方法中获取完整的“Site”对象。 但我有近 6000 次调用“siteService.getSiteById()”,并且出现如下错误:资源不足。

你有更好的方法吗,

谢谢

【问题讨论】:

  • 这就是 switchMap 的用途..
  • 如果您必须进行 6000 次调用,那么它是一个糟糕的架构。让你的 getList 返回你需要的东西
  • 在多个item的循环中调用API不是一个好习惯,但是在选择任何特定节点时调用一个API可以达到相同的效果

标签: angular rxjs6


【解决方案1】:

如果您需要进行 6000 次单独的 HTTP 调用,您可能需要修改后端以在单个请求中返回所有信息。同时,您可以尝试使用 RxJS 的 switchMap 运算符和 concatMap 以及 fromof 函数来顺序执行请求。

import { of, from } from 'rxjs';
import { switchMap, concatMap, finalize } from 'rxjs/operators';

public getAllNodes() {
  this.dataSource.data = [];
  let sites = [];
  this.nodeService.getList().pipe(
    switchMap(nodes => {
      let requests = [];
      this.dataSource.data = nodes;
      nodes.forEach(node => requests.push(this.siteService.getSiteById(node.site_id)));
      return from(requests);
    }),
    concatMap(request => of(request)),
    finalize(() => {
      this.dataSource.data.forEach((node, index) => node.site = sites[index]);
    })
  ).subscribe(
    site => {
      sites.push(site);
    },
    error => {
      // handle error
    }
  );
}

现在这6000个请求会按顺序完成,所以太慢了。

更新:并发请求使用mergeMap

正如 Will Alexander 在 cmets 中所建议的,您可以使用 RxJS mergeMap 运算符来处理并发请求。但我建议您将其保持在 6 以下,因为大多数浏览器都对同时请求到唯一域的数量有硬性限制。

所以你可以替换

concatMap(request => of(request))

mergeMap(request => of(request), null, 5)   // <-- 5 concurrent requests

【讨论】:

  • 我建议让该方法返回 Observable 而不是订阅它。您还可以使用带有并发限制值的 mergeMap 以允许多个并行请求。
  • @WillAlexander:我已经更新了答案以包含mergeMap,但返回 observable 对我没有任何影响,除了结构变化。我们可以在服务中定义整个函数,并且只向组件公开最终的 observable,但这取决于 OP。
  • 我有一个错误:TS2345:'void' 类型的参数不可分配给'ObservableInput' 类型的参数在从...返回的行中
  • @pasgeek:我错误地返回了nodes 数组而不是requests 数组。我已经修改了代码。
  • 感谢@MichaelD 的回答,但结果并不好,我得到了:Node = {id: 2, name: "008", site: Observable, site_id: 5},站点属性是可观察的,而不是我想要的对象
【解决方案2】:

类似的东西:

public getAllNodes() {
  return this.nodeService.getList().pipe(

    // emit all elements of the array separately
    map(nodes => from(nodes)),
    
    // subscribe to each Observable in turn
    // allow 5 concurrent requests
    mergeMap(node$ => node$, null, 5).pipe(

      // for each node, get the corresponding site, and emit the full object
      concatMap(
        node => this.siteService.getSiteById(node.site_id),
        (node, site) => { ...node, site })
    ),

    // when Observable completes, emit all emissions as an Array
    toArray()
  );
}

更新:考虑到 getList() 返回 Observables 的事实

【讨论】:

  • mergeMap 中的节点是可观察的,所以 node.site_id 会导致错误
  • 哦,你的 getList 返回一个 Observables 数组?
  • this getList 方法:public getList(): Observable { return this.http.get(URL); }
  • 这个怎么样?
  • 现在我在 mergeMap 运算符中出现此错误(node$ 未知):TS2769:没有重载匹配此调用。重载 1 of 3, '(project: (value: unknown, index: number) => ObservableInput, resultSelector: undefined, concurrent?: number): OperatorFunction',给出以下错误。类型“未知”不可分配给类型“ObservableInput”。
猜你喜欢
  • 1970-01-01
  • 2020-11-16
  • 2020-10-17
  • 1970-01-01
  • 1970-01-01
  • 2016-10-09
  • 2018-04-19
  • 2021-11-18
  • 1970-01-01
相关资源
最近更新 更多