Как правильно связать наблюдаемые Rx JS вместе с a для l oop? - PullRequest
1 голос
/ 24 апреля 2020

У меня есть projectService для извлечения проектов. Каждый проект, в свою очередь, имеет методы для извлечения соответствующих тегов и модулей.

Что я пытаюсь сделать, это заполнить проекты свойств экземпляра: Project [] всеми соответствующими тегами и модулями и, когда все заполнено, вызвать другой метод, который зависит от данных.

Я знаю, что мой подход не работает, потому что моя операция с картой возвращает проекты до того, как наблюдаемые в для l oop завершены, но я понятия не имею, как это сделать правильно.

class ProjectService {
  getAll(): Observable<Project[]> {...}
}
class Project {
  tagCollection: Tag[];
  moduleCollection: Module[];

  getTags(): Observable<Tag[]> {...}
  getModules(): Observable<Module[]> {...}
}
this.projectService
  .getAll()
  .pipe(
    map(projects => {
      for (const project of projects) {
        project.getTags().subscribe(tags => {
          project.tagCollection = tags;
        });
        project.getModules().subscribe(modules => {
          project.modules = modules;
        });
      }
      return projects;
    })
  )
  .subscribe(projects => {
    this.projects = projects;
    this.someOtherMethod();
  });

Обновление

Я испробовал оба решения и отредактировал их, чтобы сохранить тип проекта и использовать аналогичный стиль кодирования. Оба решения работают и, кажется, делают то же самое в контексте моего проекта. Но я не уверен, какое решение лучше, и если внесенные мной изменения нарушают какие-либо реактивные передовые методы. Кто-нибудь может уточнить это?

Решение 1 от Reqven

this.projectService
  .getAll()
  .pipe(
    switchMap(projects =>
      combineLatest(
        projects.map(project =>
          combineLatest([project.getTags(), project.getModules()]).pipe(
            map(([tags, modules]) => {
              project.tagCollection = tags;
              project.modules = modules;
              return project;
            })
          )
        )
      )
    )
  )
  .subscribe(projects => {
    console.log(projects);
    this.projects = projects;
    this.someOtherMethod();
  });

Решение 2 от NateDev

this.projectService
  .getAll()
  .pipe(
    mergeMap(projects => from(projects)),
    mergeMap(project => 
      combineLatest([project.getTags(), project.getModules()]).pipe(
        map(([tags, modules]) => {
          project.tagCollection = tags;
          project.modules = modules;
          return project;
        })
      )
    ),
    toArray()
  )
  .subscribe(projects => {
    console.log(projects);
    this.projects = projects;
    this.someOtherMethod();
  });

Ответы [ 2 ]

0 голосов
/ 25 апреля 2020

Существует множество способов асинхронного выполнения пакетных вызовов.

Итак, мне нравится выравнивать мои данные, так как я считаю, что легче выполнять операции с более плоской структурой. После того, как я выполнил операции над объектами, я перегруппировал / собрал их, используя оператор toArray

Если у вас есть какие-либо вопросы относительно операторов, которые я использовал, не стесняйтесь спрашивать. Я настоятельно рекомендую вам поиграть с различными rx js операторами преобразования .

Имейте в виду, что мой код ниже определенно не является лучшим решением, но оно отлично работает!

Надеюсь, оно работает для вас! StackBlitz

import { Component, Input, OnInit } from '@angular/core';
import { Observable, of, from, combineLatest } from 'rxjs'
import { delay, map, mergeMap,switchMap, toArray } from 'rxjs/operators';

@Component({
  selector: 'hello',
  template: `<h1>Hello {{name}}!</h1>`,
  styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent implements OnInit {
  @Input() name: string;

  data: Partial<Project>[] =  [
    { name: 'foo'},
    { name: 'foo'}
  ]

  ngOnInit() : void {
    this.getProjects().pipe(
      mergeMap(projects => from(projects)),
      mergeMap((project) => {
        const tag$ = this.getTagsByProjectName(project.name);
        const module$ = this.getModuleByProjectName(project.name);
        return combineLatest([tag$,module$]).pipe(map(([tag,module]) => ({...project, tag, module})))
      }),
      toArray()
    ).subscribe({
      next: projects => {
        console.log(projects);
        this.name = JSON.stringify(projects);
      }
    })
  }

  getProjects() : Observable<Partial<Project>[]> {
    return of(this.data).pipe(
      delay(100)
    )
  }

  getTagsByProjectName(name: string){
    const tag = 'bar';
    return of(tag).pipe(delay(100));
  }

  getModuleByProjectName(name: string){
    const module = 'baz';
    return of(module).pipe(delay(100));
  }
}

interface Project {
  name: string; 
  tag: string; 
  module: string;
}
0 голосов
/ 24 апреля 2020

Будет ли что-то подобное работать для вас?
Может потребоваться некоторая настройка, чтобы соответствовать вашему проекту, но, похоже, он работает.

this.projectService.getAll()
  .pipe(
    switchMap(projects => !projects.length
      ? of([])
      : combineLatest(
          projects.map(project => combineLatest([
            project.getTags(),
            project.getModules()
          ]).pipe(
            map(([tags, modules]) => ({tags, modules})),
            map(data => ({
              ...project,
              tagCollection: data.tags,
              moduleCollection: data.modules
            })),
        ))
    ))
  ).subscribe(projects => {
    this.projects = projects;
    this.someOtherMethod();
  });
...