Angular 9 Подписаться внутри подписаться - PullRequest
0 голосов
/ 21 июня 2020

Я работаю с Angular 9 и rx js 6, и я пытаюсь получить список узлов:

NodeService.ts

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

Nodes Entity:

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. Но у меня почти 6000 звонков на «siteService.getSiteById ()», и у меня есть такая ошибка: Недостаточно ресурсов.

У вас есть способ сделать это лучше,

Спасибо

Ответы [ 2 ]

0 голосов
/ 22 июня 2020

Что-то вроде этих строк:

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

0 голосов
/ 21 июня 2020

Если вам нужно сделать 6000 отдельных HTTP-вызовов, вам, возможно, придется изменить бэкэнд, чтобы вернуть всю информацию в одном запросе. Между тем, вы можете попробовать использовать оператор Rx JS switchMap с concatMap с from и of функции для выполнения запросов последовательно.

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

Как предложил Уилл Александер в комментариях, вы можете использовать оператор Rx JS mergeMap для одновременных запросов. Но я бы посоветовал вам оставить его меньше 6, поскольку в большинстве браузеров есть жесткое ограничение на количество одновременных запросов к уникальному домену.

Так что вы можете заменить

concatMap(request => of(request))

на

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