事件稀释 Debounce & Throttle

事件稀释是一个在实际开发过程中经常遇到的问题,例如一次鼠标滚动可能触发几十次滚动事件,当我们在懒加载的时候如果不做稀释的话,很可能用户滚动一下鼠标就会发出几十次ajax请求,这简直是一场灾难。

稀释原理

比较常见的解决方法是 DebounceThrottle。两者的目的都是为了稀释事件,但是原理有所不同。

Debounce函数的理念是“推迟执行”,即把N次连续事件只执行一次。

Throttle函数的理念是“时间稀释”,即N次连续事件在规定时间内最多执行一次。

举个例子来说,如果把事件触发比作是乘客,而事件的执行是公交车的话,当我们连续滚动屏幕不停触发'scrolling'事件的时候,就相当于来一个乘客,上一辆公交车,就发车,一辆公交车只搭载一名乘客就出发了。

Debounce 函数则是“推迟发车”,即我们规定公交车等1分钟,每来一名乘客,公交车就多等一分钟,这样只要乘客到达的时间间隔小于1分钟,公交车就会无限等待下去,直到某一次两名乘客到达的时间间隔大于1分钟,公交车发车了。如果不来乘客,公交车就不发车。

Throttle函数则是对于时间的稀释,对于Throttle的公交车,即使有乘客,那么每一分钟最多只发车一次,即使上一名乘客是59秒达到而下一名乘客是1分01秒到达,公交车依旧会在1分钟时准时发车,下一名乘客只能等待下一班车了。同样,如果不来乘客,公交车就不会发车。

下面我希望能通过一些函数Demo来说明DebounceThrottle

Debounce

这里给出了一个Debounce函数的示例,我是用class的方式实现的

class Debounce {
  handleFunction: Function;
  time: number | string;
  handler: number;
  context: object;

  constructor(handleFunction: Function, time: number | string, context?:Object) {
    this.handleFunction = handleFunction || (() => { });
    this.time = +time || 0;
    // 解决this指针作用域
    this.context = context || {};
    this.carry = this.carry.bind(this);
    this.clear = this.clear.bind(this);
    // setTimeout 句柄
    this.handler = 0;
  }

  carry(): void {
    if (this.handler) {
      this.clear();
    }

    this.handler = setTimeout(() => {
      this.clear();
      this.handleFunction.apply(this.context);
    }, this.time);
  }

  clear(): void {
    clearTimeout(this.handler);
    this.handler = 0;
  }
}

核心是Debounce.prototype.carry函数,我们可以看到,在每次调用的时候首先检测当前有没有正在执行的Debounce函数,如果有的话,则取消上一次的执行,再添加一次新的延迟执行函数,从而达到上述效果。

如果你想测试一下上面的函数,可以用以下方式调用:

import Debounce from 'path/to/debounce';

const a = new Debounce(function a() { 
  console.log("hi");
}, 1000);

window.addEventListener('scroll', a.carry);

再新建个HTML,然后把body的height设置大点就OK了。

Throttle

同样给出Throttle函数

class Throttle {
  handleFunction: Function;
  time: number | string;
  handler: number;
  context: object;

  constructor(handleFunction: Function, time: number | string, context?:Object) {
    this.handleFunction = handleFunction || (() => { });
    this.time = +time || 0;
    // 解决this指针作用域
    this.context = context || {};
    this.carry = this.carry.bind(this);
    this.clear = this.clear.bind(this);
    // setTimeout 句柄
    this.handler = 0;
  }

  carry(): any {
    if (this.handler) {
      return;
    }
    
    this.handler = setInterval(() => {
      this.handleFunction.apply(this.context);
      this.clear();
    }, this.time);
  }

  clear(): void {
    clearInterval(this.handler);
    this.handler = 0;
  }
}

export default Throttle;

可以看到其中的carry函数在规定时间内最多执行一次。

最近在项目中遇到的一些坑

其实这篇文章在写出以后确实在项目中遇到了一些问题,所以再看这篇文章写的还是有些不太全面。对于事件稀释,Throttle和Debounce都不过是稀释方法的一种。大致思想都是在对于在某一段时间内频繁触发的事件合并为一次进行请求。但是实际业务中的事件稀释方式要多得多。对于长页面的lazy load,不一定需要按时间为单位,亦可以按请求为单位,因为我们需要避免的事一秒发出几十次请求。对于不同的场景,我们要分析具体需要稀释的事情是什么。在错误的场景使用不合适的稀释方式可能反而会造成性能问题。事件稀释的精髓在于对于“事件”的稀释,不要拘泥于形式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容