将 MergeMap 与 SwitchMap 结合使用以从 NgRx Store 生成结果

Using MergeMap with SwitchMap to result from NgRx Store

我有 3 个选择器:

  1. getUserInfo => 获取帐户详细信息(示例输出:{acntId: 'A1'}
  2. getAllDepartments => 获取所有部门 ID 的列表(示例输出:['d1','d2']
  3. getAllDeptManagers => 获取每个部门 Id 的部门经理列表。

现在,我写了下面的代码:

 this.store
        .select(getUserInfo)
        .pipe(
          switchMap((res) => this.store.select(getAllDepartments, { account: res.acntId})),
          mergeMap(deptId => this.store.select(getDepartmentManagers,{departmentId: deptId }))
        )
        .subscribe((depts) => {
          console.log(depts);
        })
    );

根据我的理解,mergeMap 获取数组并相应地调用 function 并展平返回的可观察数组。

我在每次调用选择器 getAllDeptManagers 时得到 ['d1','d2'] 。我期待的是 d1 然后是 d2 等等,然后作为控制台的 depts 一次性获得所有响应。

请帮忙

mergeMap 不会展平其内部 Observable 的输出。它只是在内部 Observable 发射时重新发射,仅此而已。所以看起来你想在这里使用 forkJoin:

mergeMap(deptIds => forkJoin(
  deptIds.map(deptId => this.store.select(getDepartmentManagers, {
    departmentId: deptId
  }).pipe(take(1)))
)),

然后观察者将收到 deptIds 的单个结果数组,因为 forkJoin 将等待所有内部 Observbles 完成。

实现所需内容的最简单方法是将数组映射到流中。第一个 switchMap 获取一个值并将其映射到流上。该流发出一个数组,所以你只需要一个 mergeMap。像这样:

this.store.select(getUserInfo).pipe(
  switchMap(res => this.store.select(getAllDepartments, { account: res.acntId})),
  mergeMap(depIdArr => depIdArr), // <- The extra bit
  map(deptId => this.store.select(getDepartmentManagers,{departmentId: deptId })),
  // Really, your source observables should be competeing, 
  // but if they don't take(1) should ensure that they do.
  mergeMap(depMan$ => depMan$.pipe(take(1))),
  toArray()
).subscribe(console.log);

那个 mergeMap 看起来很有趣 (额外的位),但是如果你 return 一个数组,它会被转换成一个流。它在语义上与 mergeMap(depIdArr => from(depIdArr)) 相同,但性能更高。

如果将数组转换为流然后再转换回数组太多了,您可以将这些步骤与 zip()combineLatest() 或在这种情况下最好的方法结合起来:forkJoin()

this.store.select(getUserInfo).pipe(
  switchMap(res => this.store.select(getAllDepartments, { account: res.acntId})),
  map(deptIdArr => deptIdArr.map(
    deptId => this.store.select(
      getDepartmentManagers,{departmentId: deptId }
    )
  )),
  // Really, your source observables should be completing, 
  // but if they don't take(1) should ensure that they do.
  map(deptIdArrS => deptIdArrS.map(
    deptId$ => deptId$.pipe(take(1))
  ),
  mergeMap(deptIdArrS => forkJoin(deptIdArrS))
).subscribe(console.log);