0

我在使用rxjs. 上MyActions.myAction,我收到一个包含一个属性的对象ids- 一个 id 数组 - 对于每个 id 我想通过它发送一个 HTTP 请求this.myApiService.getResource,它返回一个Observable<Resource>. 然后我想将所有结果收集到一个数组中,并调度另一个传递数组的动作。

  public loadResources$: Observable<MyAction> = this.actions$.pipe(
    ofType(MyActions.myAction),
    switchMap(({ ids }) => from(ids).pipe(
      mergeMap(id => this.myApiService.getResource(id)),
      toArray()
    )),
    map(resources) => MyActions.resourcesLoaded({ resources } )),
  );

上面的代码完成了这项工作,但我想知道是否应该避免嵌套两个反应式运算符流,以及是否有更好的方法来编写它。

我想知道的原因是我在为它编写测试时遇到了问题。我在下面写了测试,但我不能让它通过。

 it('should dispatch an resourcesLoaded action with the resources', () => {
      const ids = ['5f7c723832758b859bd8f866'];
      const resources = [{} as Resource];

      const values = {
        l: MyActions.loadResources({ ids }),
        t: ids[0],
        o: MyActions.resourcesLoaded({ resources })
      };

      actions =         hot('--l------------', values);
      const get$ =     cold('  -------t-----', values);
      const expected = cold('---------o-----', values);

      myApiService.getResource.withArgs(ids[0]).returns(get$);

      expect(myEffects.loadResources$).toBeObservable(expected);
    });

我得到的错误是:

     Expected $.length = 0 to equal 1.
Expected $[0] = undefined to equal Object({ frame: 50, notification: Notification({ kind: 'N', value: { ....
Error: Expected $.length = 0 to equal 1.
Expected $[0] = undefined to equal Object({ frame: 50, notification: Notification({ kind: 'N', value: { ....
    at <Jasmine>
    at compare (http://localhost:9876/Users/jacopolanzoni/Documents/Development/myProject/node_modules/jasmine-marbles/index.js:91:1)
    at <Jasmine>
4

2 回答 2

0

但我想知道是否应该避免嵌套两个反应式运算符流,以及是否有更好的方法来编写它

我会说这取决于你想要达到的目标,至少在这种情况下是这样。

of([1,2,3]).pipe(mergeAll(), switchMap(value => http.get(...)))

不同于

of([1,2,3]).pipe(switchMap(ids => from(ids).pipe(mergeMap(...))))

在第一种情况下,每个内部 observable 都将被下一个值丢弃(最后一个值除外),因此只有 3 个会解析。
在第二种情况下,它将处理所有这些,因为您在内部 observable 中分解数组(由 管理swtichMap,因此丢弃其内部 observable 的唯一方法是如果一个新的外部值(例如另一个 id 数组)由源发出)。

不需要嵌套的情况是:

of([1,2,3])
  .pipe(
    // whenever you want to explode an array,
    // it does not matter which higher order operator you use
    // since the operation is **synchronous**
    // so, `mergeAll`, `concatAll`, `switchAll` should work the same
    mergeAll(),

    mergeAll(id => this.apiService.useId(id))
  )

// same as

of([1,2,3])
  .pipe(
    mergeMap(ids => from(ids).pipe(mergeMap(id => this.apiService.useId(id))))
  )

如您所见,在这种情况下,switchMap已替换为mergeMap.

于 2021-01-10T16:17:47.600 回答
0

我发现我的测试失败了,因为toArray正在等待getResource(ie, httpClient.get) 返回的 observable 完成。替换t(t|)修复测试:

 it('should dispatch an resourcesLoaded action with the resources', () => {
      const ids = ['5f7c723832758b859bd8f866'];
      const resources = [{} as Resource];

      const values = {
        l: MyActions.loadResources({ ids }),
        t: ids[0],
        o: MyActions.resourcesLoaded({ resources })
      };

      actions =         hot('--l------------', values);
      const get$ =     cold('  -------(t|)-----', values);
      const expected = cold('---------o-----', values);

      myApiService.getResource.withArgs(ids[0]).returns(get$);

      expect(myEffects.loadResources$).toBeObservable(expected);
    });

然而,我的问题的第一部分,即这样嵌套操作符是否是好的做法,仍然存在。

于 2021-01-10T12:44:21.430 回答