簡體   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