首先理解 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 的原创文章,尽在:” 汪子熙 ”: