关于rxjs:combineLatest-使用的一个陷阱和基于-debounceTime-的解决方案

42次阅读

共计 1763 个字符,预计需要花费 5 分钟才能阅读完成。

首先理解 combineLatest 这个操作符的作用:

组合多个 Observable 以创立一个 Observable,其值是依据其每个输出 Observable 的最新值计算得出的。

其弹珠图如下图所示:

咱们有一个限度值流和一个偏移值流。咱们应用 combineLatest 组合这些流以创立一个流,该流将在每次源流之一更改时具备一个新值。而后咱们应用 switchMap 依据这些值从后端获取数据以获取 pokemon$。因为咱们应用了 switchMap,如果一个调用还没有完结,那么当一个新的调用通过扭转 limit 或者 offset 来发动一个新的调用时,前一个调用就会被勾销。

代码如下:

this.pokemon$ = combineLatest(limit$, offset$)
       .pipe(map(data => ({limit: data[0], offset: data[1]})),
        switchMap(data => this.pokemonService.getPokemon(data.limit, data.offset)),
        map((response: {results: Pokemon[]}) => response.results),
      );

代码地址如下:

https://stackblitz.com/edit/a…

当我批改 limit 和 offset 为其余值之后,点击 reset 按钮,此时会察看到先后发动两个申请,并且第一个申请主动被 cancel 的状况:

通过单击重置按钮,咱们通过同时重置限度和偏移值来更新咱们的两个源流。这个动作的成果是 combineLatest 创立的流触发了两次,因而启动了两个后端申请,另一方面,因为咱们应用了 switchMap,立刻勾销了一个。

咱们来单步拆解,以加深印象:

  • combineLatest 保留所有源流的最初一个值。比方开始场景是,limit = 8,offset = 2)
  • 单击重置按钮
  • limit 设置为 5
  • combineLatest 看到一个新值进入 limit 并收回一个新组合,limit = 5,offset = 2
  • switchMap 获取这些值并订阅触发后端调用的流
    偏移量设置为 0
  • combineLatest 看到一个新的 offset 值,并收回一个新的组合,limit = 5,offset = 0
  • switchMap 获取这些值,勾销订阅(并因而勾销)先前的申请并开始新的申请

在此流程中您可能没有预料到的是,无论何时设置 limit,此更改都会在更改 offset 之前间接流传到 combineLatest.

如何防止这个行为

如果有一种办法能够确保在同一个调用堆栈中产生的更改(这是单击重置按钮时产生的状况)被抛弃以反对最初一次更改,咱们能够解决咱们的问题。

这意味着,当 combineLatest 在同一个调用堆栈中收回两个值时,当调用堆栈被革除时,最初一个值将被发送。

为此,咱们能够在 combineLatest 之后间接利用值为 0 的 debounceTime。这将确保只有最初一个值被传递给 switchMap,并且在调用堆栈被革除之后。

每当提到“在同一个调用堆栈中”时,都能够将其替换为“在事件循环的同一轮次中产生的更改”。

加上了 debounceTime(0) 之后的时序图:

  • combineLatest 保留所有源流的最初一个值,开始场景是,limit = 8,offset = 2
  • 单击重置按钮
  • 限度设置为 5
  • combineLatest 运算符看到一个新值进入 limit 并收回一个新组合,limit = 5,offset = 2
  • debounceTime 运算符看到一个新值,并且(因为操作符的值为 0)将期待直到调用堆栈被革除以将其传递
  • 偏移量设置为 0
  • combineLatest 运算符看到一个新的 offset 值,并收回一个新的组合,limit = 5,offset = 0
  • debounceTime 运算符再次看到一个新值,将抛弃旧值,并期待堆栈被革除以将其传递
  • 调用堆栈被革除
  • debounceTime 运算符没有看到新的值,将通过组合,limit = 5,offset = 0 向上游发送数据
  • switchMap 操作符获取这些值并订阅触发后端调用的流

修复代码非常简单,加上一行代码即可:

debounceTime(0),

修复后的成果,点击 reset 按钮之后,只有一次 HTTP 申请收回了:

更多 Jerry 的原创文章,尽在:” 汪子熙 ”:

正文完
 0