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

举报
汪子熙 发表于 2022/10/03 10:09:16 2022/10/03
【摘要】 首先了解 combineLatest 这个操作符的作用:组合多个 Observable 以创建一个 Observable,其值是根据其每个输入 Observable 的最新值计算得出的。其弹珠图如下图所示:我们有一个限制值流和一个偏移值流。 我们使用 combineLatest 组合这些流以创建一个流,该流将在每次源流之一更改时具有一个新值。 然后我们使用 switchMap 根据这些值从后...

首先了解 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/angular-deqtkx

当我修改 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 请求发出了:

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。