61

下面是来自 Ngrx 示例的代码:https ://github.com/ngrx/example-app/blob/master/src/effects/book.ts我的问题是为什么在第一个@Effect 中,它使用switchMap而其他人使用mergeMap. 那是因为第一个@Effect 正在处理网络,并且switchMap如果它正在运行,您可以取消之前的网络请求?

@Effect() search$ = this.updates$
    .whenAction(BookActions.SEARCH)
    .map<string>(toPayload)
    .filter(query => query !== '')
    .switchMap(query => this.googleBooks.searchBooks(query)
      .map(books => this.bookActions.searchComplete(books))
      .catch(() => Observable.of(this.bookActions.searchComplete([])))
    );


  @Effect() clearSearch$ = this.updates$
    .whenAction(BookActions.SEARCH)
    .map<string>(toPayload)
    .filter(query => query === '')
    .mapTo(this.bookActions.searchComplete([]));


  @Effect() addBookToCollection$ = this.updates$
    .whenAction(BookActions.ADD_TO_COLLECTION)
    .map<Book>(toPayload)
    .mergeMap(book => this.db.insert('books', [ book ])
      .mapTo(this.bookActions.addToCollectionSuccess(book))
      .catch(() => Observable.of(
        this.bookActions.addToCollectionFail(book)
      ))
    );


  @Effect() removeBookFromCollection$ = this.updates$
    .whenAction(BookActions.REMOVE_FROM_COLLECTION)
    .map<Book>(toPayload)
    .mergeMap(book => this.db.executeWrite('books', 'delete', [ book.id ])
      .mapTo(this.bookActions.removeFromCollectionSuccess(book))
      .catch(() => Observable.of(
        this.bookActions.removeFromCollectionFail(book)
      ))
    );
}
4

4 回答 4

100

你是对的; switchMap一旦Observable再次project调用该project函数以生成新的Observable.

RxJs 非常强大和密集,但它的高级抽象有时会使代码难以理解。让我稍微揭穿@Andy Hole 给出的大理石图和文档,并更新它们。您可能会发现大理石语法参考非常有价值,可以从他们的测试中更好地理解 rxjs 运算符(至少我发现官方文档中缺少/没有足够突出显示)。

合并地图

合并地图

图中的第一行是源 Observable,它(1,3,5)在不同的时间发射。图中的第二行是传递给操作符的project函数返回的原型 Observable 。i => ....mergeMap()

当源 Observable 发出 item1时,使用mergeMap()调用project函数i=1。返回的 Observable 将发射103 次,每 10 帧(请参阅大理石语法参考)。当源 Observable 发出项目3并且project函数创建一个发出303 次的 Observable 时,也会发生同样的情况。请注意, 的结果mergeMap()包含从 . 返回的每个 Observable 生成的所有三个元素project

切换地图

切换地图 这与 不同,一旦在新元素上再次调用它switchMap(),它将取消订阅由返回的 Observable 。project大理石图表明了这一点,输出 Observable 中缺少第三30项。

在您给出的示例中,这会导致挂起的搜索请求被取消。这是一个非常好的但很难获得正确的属性,您可以通过结合switchMap()AngularHttp服务返回的可取消的 Observables 来免费获得它。这可以为您省去很多麻烦,而不必担心正确处理异步取消通常发生的所有竞争条件。

于 2017-02-14T13:24:47.657 回答
7

你说的对。

如您所见,switchMap 与搜索功能一起使用。此示例中的搜索框被编程为当用户在文本框中输入文本时(具有 350 毫秒的去抖动或延迟)基本上发出搜索请求。

这意味着当用户输入“har”时,ngrx 会向服务发送搜索请求。当用户输入另一个字母 'r' 时,之前的请求被取消(因为我们不再对 'har' 感兴趣,而是对 'harr' 感兴趣)。

在另一个答案中提供的大理石图中很好地显示了这一点。在mergeMap 中,之前的Observables 没有被取消,因此'30' 和'50' 混合在一起。使用 switchMap,只有 5 被发射,因为 3 被取消了。

于 2016-09-22T09:23:28.200 回答
4

合并地图

将每个源值投影到一个 Observable,该 Observable 被合并到输出 Observable 中。

将每个值映射到一个 Observable,然后使用 mergeAll 展平所有这些内部 Observable。

在此处输入图像描述

切换地图

将每个源值投影到一个 Observable,该 Observable 合并到输出 Observable 中,仅从最近投影的 Observable 发出值。

将每个值映射到一个 Observable,然后使用 switch 展平所有这些内部 Observable。

在此处输入图像描述

来源:RxJS 中的 ES6 Observables

于 2016-07-27T15:58:50.217 回答
0

您不希望取消 API 保存数据请求。这就是您要使用mergeMap 的原因。可以丢弃搜索查询,不会丢失数据,并且用户可能正在编辑他们的查询并且对旧查询的数据不感兴趣。因此 switchMap。

于 2020-06-25T16:36:03.810 回答