将多个可观察对象合并到一个RxJS流中

时间:2020-01-04 04:13:01

标签: javascript angular rxjs observable

我似乎不知道该使用哪个RxJS运算符来解决以下问题:

在我的音乐应用程序中,我有一个提交页面(就像一个音乐专辑)。要加载提交,我使用以下查询:

this.submissionId = parseInt(params['album']);

if (this.submissionId) {
  this.submissionGQL.watch({
    id: this.submissionId
  }).valueChanges.subscribe((submission) => {
      //submission loaded here!
  });
}

很容易!但是,一旦加载了submission,就必须加载一些辅助信息,例如当前的user(以检查它们是否是提交的艺术家)和comments。为了避免嵌套订阅,我可以修改以上查询,以在switchMap解析后使用user将查询流切换到commentssubmission观察对象:

// stream to query for the submission and then switch query to user
this.submissionGQL.watch({
    id: this.submissionId
  }).valueChanges.pipe(
  switchMap(submission => {
    this.submission = submission;
    return this.auth.user$
  })
).subscribe((user) => {
  // needs value of submission here
  if (user.id == this.submission.user.id) {
    //user is owner of submission
  }
})

// stream to query for the submission and then switch query to comments
this.submissionGQL.watch({
    id: this.submissionId
  }).valueChanges.pipe(
  switchMap(submission => {
    this.comments$ = this.commentsGQL.watch({
      submissionId: submission.id //needs submission response here
    })
    return this.comments$.valueChanges
  })
).subscribe((comments) => {
  this.comments = comments;
})

太好了!现在,我已经避免了嵌套订阅问题,但是每个submission请求的第一部分是相同的。基本上,一旦查询submission,我就想启动两个并行查询:

  • 针对用户的查询
  • 查询评论

哪个RxJS运算符可以执行这样的操作?我想最后的订阅会发出一个数组响应,如:

.subscribe([user, comments] => {
    // check if user == submission.user.id here
    // also assign comments to component variable here
})

我相信mergeMap是我所需要的,但是我不确定如何正确实现。还是在这种情况下,我应该share()提交查询,然后分别构建并行查询?我很好奇!请让我知道,谢谢!

2 个答案:

答案 0 :(得分:0)

尝试:

  this.submissionGQL.watch({
    id: this.submissionId
  }).valueChanges.pipe(
   switchMap(submission => {
     this.submission = submission;
     const user$ = this.auth.user$;
     this.comments$ = this.commentsGQL.watch({
       submissionId: submission.id 
     });
     return combineLatest(user$, this.comments$);
   }),
   // maybe put a takeUntil to remove subscription and not cause memory leaks
  ).subscribe(([user, comments]) => {
    // check if user == submission.user.id here
    // also assign comments to component variable here
 });

您应该考虑的是借助Angular(https://malcoded.com/posts/angular-async-pipe/)提供的async管道来消除实例变量。 它将订阅可观察对象,将其显示在视图中,并在视图被破坏时自动退订。

因此,使用该代码,我们可以通过放置以下内容来摆脱this.submissions = submission

submissions$: Observable<ISubmission>; // assuming there is an interface of ISubmission, if not put any

// then when this.submissionId is defined
this.submissions$ = this.submissionGQL.watch({
  id: this.submissionId
}).valueChanges;

// then when using it in your view you can do {{ this.submissions$ | async }}

this.comments$同样适用。尽管所有这些都是可选的。在使用RxJS时,我会尽量减少实例变量,因为太多的实例变量会导致混乱。

然后,您可以从this.submissions$开始观察并订阅其他主流。

 this.submission$.pipe(
  switchMap(submission => ..... // everything else being the same
 )

我选择了combineLatest运算符,但您可以根据需要使用zipforkJoin。它们都有细微的差异(https://scotch.io/tutorials/rxjs-operators-for-dummies-forkjoin-zip-combinelatest-withlatestfrom)。

答案 1 :(得分:0)

在这种情况下,您可以使用RxJS forkJoin运算符。如文档所述,

所有可观测值完成后,从每个可观测值中发出最后一个发出的值。

const userQuery$ = this.submissionGQL.watch({
    id: this.submissionId
  }).valueChanges.pipe(
  switchMap(submission => {
    this.submission = submission;
    return this.auth.user$
  })
)

// stream to query for the submission and then switch query to comments
const commentsQuery$ = this.submissionGQL.watch({
    id: this.submissionId
  }).valueChanges.pipe(
  switchMap(submission => {
    this.comments$ = this.commentsGQL.watch({
      submissionId: submission.id //needs submission response here
    })
    return this.comments$.valueChanges
  })
)

forkJoin(userQuery$, commentsQuery$).subscribe([user, comments] => {
  // check if user == submission.user.id here
  // also assign comments to component variable here
})