Rxjs防抖与节流在项目中的应用

背景一:当前项目引用了tinyMce以及公式编辑器,在实际的使用中发现,有些题目可能需要较长的时候来填写。由于填写的时间超出了cookie的过期时间,所以当用户千辛万苦的填写完以后,点击保存按钮时发生了401。再回来原来的界面,一切归0。虽然当前已经采用了类似于github的处理方式,但由于在添加、编辑题目时的特殊性,实际的体验并不好。

背景二:当前项目对安全性要求比较高,我们希望10分钟内如果没有监测到用户的操作记录的话,进行锁屏的处理。

虽然在没有充分的使用Rxjs之前,该功能也能够实现,但Rxjs的防抖与节流功能可以更简单的满足上述需求。

节流 throttleTime

为了解决背景一,我们提出了新的想法:用户编辑tinyMce的内容时,组件使用了特定的方法进行接收。在这个接收方法中,我们记录最新接收值的时间,并去对应触发后台的心跳方法。这样以来,就保障了用户在编辑内容时充分地与后台进行交互,而不会在保存时由于过长时间未与后台交互发生的401问题了。

而此方法虽然能够解决401的问题,但却造成了大量的冗余请求。而实际上若要保证cookie有效期,只要保证适时的与后台进行交互即可。Rxjs的节流
throttleTime可以很好的满足当前的要求。

套用官方文档的一张图来简单说下:throttleTime
image.png

如图:在throttleTime操作符下,设置了间隔为50ms。上图中axybxcx分别在第0,10,20,60,100,100ms时发射了数据。

  • a 第一个发射的数据,不过滤,发送给订阅者。
  • x 10ms发射的数据,距离(上一次成功发射)a间隔小于50ms,过滤,不发送给订阅者。
  • y 20ms发射的数据,与a间隔小于50ms,过滤,不发送给订阅者。
  • b 60ms发射的数据,与a间隔不于小50ms,不过滤,发送给订阅者。
  • x 100ms发射的数据,与(上一次成功发射)b间隔小于50ms,过滤,不发送给订阅者。
  • c 110ms发射的数据,与b间隔不小于50ms,不过滤,发送给订阅者。

依此理论,一个心跳的服务大概是这个样子:

import { Injectable } from '@angular/core';
import { ReplaySubject } from 'rxjs';
import { throttleTime } from 'rxjs/operators';
import { UserService } from './user.service';


/**
 * 心跳服务
 */
@Injectable({
  providedIn: 'root'
})
export class HeartbeatService {

  private interval = 15 * 60 * 1000; // 时间间隔
  private heartbeatSubject = new ReplaySubject(1);

  constructor(private userService: UserService) {
    this.onInit();
  }

  /**
   * 初始化心跳功能
   * https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-auditTime
   * https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-throttleTime
   */
  onInit() {
    this.heartbeatSubject.asObservable().pipe(throttleTime(this.interval))
      .subscribe(() => {
        this.userService.sendHeartbeat();
      });
  }

  send(): void {
    this.heartbeatSubject.next();
  }
}

其它预发送心跳的组件直接调用send()方法即发成了心跳的发射。

auditTimethrottleTime相似,推荐同步学习。

防抖 debounceTime

情景二:如果10分钟内没有监听到用户的操作,则弹出锁屏界面。这功能使用防抖功能能够轻松的完成。

防抖功能的典型应用是实时查询,比如用户想搜索“河北工业大学”,实际的输入过程是这样:
“河” -- 请求1次后台(无用功)
“河北” -- 请求1次后台(无用功)
“河北工” -- 请求1次后台(无用功)
“...” -- 请求1次后台(无用功)
“河北工业大学” -- 请求1次后台(用户想要的)

如果不加入防抖,那么用户每输入一个字符都会请求一次后台,则用户输入完“河北工业大学”后,则需要请求6次后台。而用户最终仅想查询一次“河北工业大学”。假设用户的输入速度是1秒钟一个字符,则可以加入1秒的防抖 ---- 如果用户在1秒内又重新输入了新的字符,则忽略用户前面输入的;如果距离用户的最后输入时间大于1秒钟,则按用户最后的输入发起请求。这样便有效的规避了冗余请求的问题。

再借用官方文档的图片:
image.png
如图示:abcd的发射时间分别为:0,30,40,65ms。debounceTime定义了防抖时间为20ms,那么:

  • a 第0ms发射,并在且20ms内没有新的要发射的内容,将数据发送给订阅者。
  • b 第30ms发射,但在20ms(确切的说是在间隔10ms时)内出现了新的发射内容c,将取消发送数据。
  • c 第40ms发射,在20ms内没有新的要发射的内容,将数据发送给订阅者。
  • d 第65ms发射,在20ms内没有新的要发射的内容,将数据发送给订阅者。

具体到锁屏功能,大体上长这个样子:

  this.xxxxSubject.asObservable().pipe(debounceTime(10 * 60 * 1000))
      .subscribe(() => {
        if (用户已登录) {
          锁屏
        }
      });

  send(): void {
    this.xxxSubject.next();
  }

总结

Rxjs的操作符有很多,当前阶段只有想不到,没有人家做不到(如果它还就真的没做到,那么我们还可以自定义操作符。同时还有机会为Rxjs提交pull request而成为Rxjs的代码贡献者)。

Rxjs中文 -- 防抖debounceTime

Rxjs中文 -- 节流throttleTime

你可能感兴趣的:(rxjs)