一步一步认识debounce和throttle(防抖和节流)

其实早就听说了节流和防抖的解决方案。这两个的目的都是为了提升页面中监听事件的性能。才疏学浅,如果有错误,也希望各位指正。

一 为什么会出现debounce和throttle

在2011年的时候,有一篇报告这样说:当你向下滚动twitter页面的时候,越向下滚动就会发现页面会越来越卡顿甚至无响应。John Resig 就在一篇博客中提到,在一些高触发的事件如scroll,resize事件中,直接在监听事件内部做出响应是非常不好的举措。 https://johnresig.com/blog/learning-from-twitter/

相信,你也写过这类dom事件的监听操作,也应该清楚地知道,当你监听scroll,resize事件的时候,事件触发的频率是非常高的,(具体的时间我没有考量)。如果你监听事件直接每次都进行相关的dom操作,消耗是非常大的。看一个pen上的例子。
下面这个是监听scroll事件的操作,在监听滚动事件的时候,数字自动累计增加。
可以看到,在每一次滚动的时候,事件触发的频率是非常高的。
所以,
就有了节流和防抖机制的出现,其实这两个也没有什么特殊的,只不过是定时器的应用而已。

test.gif

二 debounce 和 throttle是什么 ?

debounce,防抖,其实就是通过设置定时器,让高频触发的事件在触发结束之后再做出相关响应。只执行一次。
throttle,节流。其实也是通过设置定时器,让高频连续触发的事件每隔一定的时间长度之后再做出响应。是有规律的时间间隔去执行。

什么意思呢? 看一下上面的gif图,原本是一直在触发的,那么我们可以通过设置debounce防抖,达到当滚动停止之后,再触发事件。
那么?是否也可以通过设置throttle节流来实现?throttle其实就是不要在事件结束之后再响应,而是在这个时间段内持续触发,待会我会给你们看一下区别

思考一下,什么时候使用防抖?什么时候使用节流呢?

  • resize scroll 还有用户点击按钮提交ajax请求的时候,都可以使用debounce防抖来提升性能。当用户狂点击提交请求的时候,我们在用户点击的时候不做出任何响应,等到用户停止点击之后我们再去提交。
  • 当然,实现无限滚动的时候,我们最好还是用节流来实现。我们总不能让用户滚动到底部之后停一下再去响应事件吧。我们要做的应该是在滚动期间每隔一定的时间段去响应判断滚动条是否已经到达底部。

三 手动实现防抖debounce

我知道目前实现得最好的是underscore.js的_debounce()方法。但是作为小白,还是一步一步慢慢进阶吧。后期再认真弄清楚源码。

1)最初级的版本:来自高程三的例子

test.gif

var common = document.getElementById('common')   // 获取页面的左边
var special = document.getElementById('special')   // 获取页面的右边
// 这是执行debounce的。
 window.onresize = function(){
   debounce(addlist ,delay)  //这一个设置了节流
   commonWay()   // 这个是普通的函数执行
}
 function debounce(fn,delay) {     // 定义一个debounce函数
     clearTimeout(fn.timeid)
      fn.timeid = setTimeout(function() {
      fn()  
     },delay)
  }   
}    
function addlist () {                // 监听事件的响应事件,执行dom操作。
   special.innerHTML+='<li>k</li>'  
}
function commonWay () {     // 这是执行了普通的函数
  common.innerHTML+='<li>k</li>'  
}

其实就是给fn设置了一个属性,timeid,记录此时的定时器。在事件触发的时候,会先清除定时器,然后再设置一个定时器,也就是只要在事件触发的时候,刚建立一个定时器就把它销毁,直到最后一次,事件触发结束之后,就执行最后一次设置的定时器。

2)稍微升级一下:用闭包存储变量
细心的你可能会发现,通过给传进来的函数添加属性来存储定时器,是不太好的,怎么说你一个函数参数传递进来,你去修改它的属性。是不好的方式。
那么。我们可以用闭包了存储当前的定时器变量。修改如下:这里只将debounce的修改放下来。

var middle = debounce(addlist ,delay)    // 先执行这个函数,返回一个函数,存储起来
window.addEventListener('resize', function () {
   middle()  //这一个设置了节流
   commonWay()   // 这个是普通的函数执行
 })
/*
@fn 是要执行的响应操作事件
@delay 是延时的时间长度
*/
function debounce(fn,delay) {
  var timeid;   // 这里定义一个变量
  return function () {    // 闭包的使用,返回一个函数。
   clearTimeout(timeid)
   timeid = setTimeout(function() {
     fn() 
 },delay)
}   
}  

四 节流的实现

当然,你会发现,单纯的实现防抖debounce效果还是有所欠缺的。如果我们在实现无限当滚动的时候,用户每次都要等到停止滚动之后才能继续加载数据,对于用户的体验是不是很不好。所以这个时候就可以用到节流了。

实现思想:事件第一次触发的时候,记录函数执行的时间,当函数再一次执行的时间间隔达到interval毫秒的时候,就执行函数。也就是每隔一定的时间,就执行一次响应函数。

// tottle的实现,也就是节流的实现,就是设置了一个一开始函数运行的时间戳进行执行
function throttle (fn, delay, mustRunDelay,context) {
  var startTime, timestamp, timer;
  return function () {
    timestamp = +new Date()   // 先设置开始的时间
    clearTimeout(timer)
    if (!startTime) {
      startTime = timestamp    
    } 
    if (timestamp - startTime >= mustRunDelay) {
      fn.apply(context)
      startTime = timestamp 
    }else {
      timer = setTimeout(function () {
      fn.apply(context)    
    },delay)    
    }
   }
}
var middle = debounce(addlist ,delay)   // 先执行函数,保存匿名函数
 var middle2 = throttle(commonWay,1000,500)  // 再次执行函数,然后保存匿名函数
 window.addEventListener('resize', function () {
   middle()
   middle2() 
})
test.gif

最后,用节流来实现无限滚动效果

// html 部分
<html>
<body>
   <div class='infinitScroll' id='list'>
      <div class='scroll'></div>
      <div class='scroll'></div>
      <div class='scroll'></div>
      <div class='scroll'></div>
    </div>
</body>
</html>
// js 部分 
function fetchdata () {   //  这里模拟数据的获取。
  var Odiv = '<div class="scroll"></div>'
  var result = ''
  for (var i =0; i<20; i++) {
    result += Odiv
  }
  return result;  
}

function cal_set () {    // 判断是否到达底部,到达底部就获取数据
    var pixelsFromWindowBottomToBottom = document.body.scrollHeight - document.body.scrollTop - document.body.clientHeight
    if (pixelsFromWindowBottomToBottom < 300){
        myContainer.innerHTML += fetchdata()
    }
}
window.onscroll = throttle(cal_set, 300)   // 滚动监听
test.gif
当然。我也用不加节流限制的,实现了滚动的监听。实现的效果如下图所示。虽然也是一样可以实现无限滚动,但是你可以看一下控制台的输出,稍微滚动一下就已经输出了上百条了。再看看上面的输出,其实只执行了几次而已。
window.onscroll = cal_set
test.gif

当然,现在也有实现这两个效果的函数,就是underscore的两个函数,debounce和throttle.因为我有稍微研究了一下,但是还有一些细节不是很懂。就不详细讲了。不过可以看一下下面分享的链接。韩迟子的underscore源码详解有做一些相关的讲解。
当然,CSS-trick的链接也有很多相关的例子,大家可以直接上去看。

引用链接:

https://css-tricks.com/debouncing-throttling-explained-examples/
https://github.com/hanzichi/underscore-analysis/issues/22
https://keelii.github.io/2016/06/11/javascript-throttle/

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

推荐阅读更多精彩内容