繁体   English   中英

如何正确地将 RxJS observables 与 for 循环链接在一起?

[英]How to properly chain RxJS observables together with a for loop?

我有一个 projectService 来检索项目。 每个项目依次具有检索相应标签和模块的方法。

我尝试做的是用所有相应的标签和模块填充一个实例属性项目:Project[],当所有内容都被填充时调用另一个方法,这取决于数据。

我知道我的方法不起作用,因为我的 map 操作会在 for 循环中的可观察对象完成之前返回项目,但我不知道如何正确执行。

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();
  });

更新

我已经尝试了这两种解决方案并对其进行了编辑以保留项目类型并使用类似的编码风格。 这两种解决方案都有效,并且在我的项目上下文中似乎做同样的事情。 但我不确定哪种解决方案更好,以及我所做的编辑是否违反了任何反应性最佳实践。 有人可以详细说明吗?

来自 Reqven 的解决方案 1

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();
  });

来自 NateDev 的解决方案 2

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();
  });

这样的事情对你有用吗?
可能需要进行一些调整以适合您的项目,但它似乎正在运行。

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();
  });

有许多不同的方法可以异步执行批处理调用。

所以我喜欢做的是扁平化我的数据,因为我相信在更扁平的结构上执行操作更容易。 完成对对象的操作后,我使用toArray运算符重新组合/重新组合它们

如果您对我使用的运算符有任何疑问,请随时提问。 我强烈建议您尝试使用不同的rxjs 转换运算符

请注意我下面的代码绝对不是最好的解决方案,但它工作正常!

希望这对你有用! 堆栈闪电战

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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM