JavaScript之防抖(Debounce)和節(jié)流(Throttle)

防抖與節(jié)流

一棋返、背景

防抖和節(jié)流是兩種不同的控制一個函數(shù)執(zhí)行次數(shù)的方法母债,其目的都是為了節(jié)約計算機資源午磁。
當我們操作DOM的時候,加上節(jié)流或者防抖就非常有必要毡们,因為眾所周知迅皇,操作DOM的開銷是非常大的,所以要盡可能減少DOM操作次數(shù)衙熔。

看下面的在線例子:
https://codepen.io/dcorb/pen/PZOZgB

當鼠標滾動或者拖拽的時候可以輕易地每秒觸發(fā)30個事件登颓,而且在移動端慢速滾動可以達到每秒100次,要是這么短的時間內每次都觸發(fā)一個或多個函數(shù)红氯,那瀏覽器應該會卡死崩潰的框咙,所以這就引出了防抖節(jié)流

二痢甘、防抖(Debounce)

防抖允許我們把多次連續(xù)的調用放到一次里面去執(zhí)行喇嘱,在事件被觸發(fā)n秒后再執(zhí)行回調,如果在這n秒內又被觸發(fā)塞栅,則重新計時者铜。

防抖

試想一下,你正身處一部電梯之中放椰,電梯門開始關閉作烟,然后突然又來了一個人,按下了開門鍵砾医,電梯門開了拿撩,他順利上了電梯,然后電梯又開始緩慢關閉如蚜,但是此時又匆匆跑來了另一個人压恒,電梯門又開了,此時错邦,快要遲到的你心里面開始有點不滿了难咕。但是五督,這其實就是一個防抖的實際例子稀蟋,電梯盡可能的使其資源利用率達到最高乘盖,在盡可能的情況下,載上盡可能多的人。

下面是一個例子:
https://codepen.io/dcorb/pen/KVxGqN

  • 具體實現(xiàn)
function debounce(func,delay) {
    let timer;
    return function(...args) {
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(() => {
            func.apply(this,arguments);
        },delay)
    }
}

Lodash中實現(xiàn) _.debounce_.throttle 的功能很全面讨勤,可以直接使用箭跳,其中的 throttle函數(shù)是使用 _.debounce 新增 maxWait` 選項來實現(xiàn)的, 有興趣可以自行查看 源碼

  • Resize實例
// Based on http://www.paulirish.com/2009/throttled-smartresize-jquery-event-handler/
$(document).ready(function(){
  
  var $win = $(window);
  var $left_panel = $('.left-panel');
  var $right_panel = $('.right-panel');
  
  function display_info($div) {
    $div.append($win.width() + ' x ' + $win.height() +  '<br>');
  }
                
  $(window).on('resize', function(){
    display_info($left_panel);
  });
  
  $(window).on('resize', _.debounce(function() {
    display_info($right_panel);
  }, 400));
});

可以用來監(jiān)聽瀏覽器窗口尺寸變化事件潭千。
https://codepen.io/dcorb/pen/XXPjpd

  • 輸入驗證實例
$(document).ready(function(){

  var $statusKey = $('.status-key');
  var $statusAjax = $('.status-ajax');
  var intervalId;
  
  // Fake ajax request. Just for demo
  function make_ajax_request(e){
      var that = this;
      $statusAjax.html('That\'s enough waiting. Making now the ajax request');
     
      intervalId = setTimeout(function(){
         $statusKey.html('Type here. I will detect when you stop typing');
         $statusAjax.html('');
         $(that).val(''); // empty field
      },2000);
    }
  
  // Event handlers to show information when events are being emitted
    $('.autocomplete')
      .on('keydown', function (){  
      
        $statusKey.html('Waiting for more keystrokes... ');
      clearInterval(intervalId);             
      })
     
  
  // Display when the ajax request will happen (after user stops typing)
    // Exagerated value of 1.2 seconds for demo purposes, but in a real example would be better from 50ms to 200ms
  $('.autocomplete').on('keydown',
       _.debounce(make_ajax_request, 1300));
  });

當用戶輸入并且發(fā)起Ajax請求的時候谱姓,_.debounce可以用來實現(xiàn)防抖,比如間隔2秒未檢測到用戶輸入才發(fā)起請求刨晴。

三屉来、節(jié)流(Throttle)

  • 具體實現(xiàn)
    使用定時器
var throttle = function(func, delay) {
    var timer = null;
    return function() {
        if (!timer) {
            timer = setTimeout(function() {
                func.apply(this,arguments);
                timer = null;
            },delay);
        }
    }
}

另一種定時器寫法:

let isAllow = true;
function throttle() {
    let fun = function() {
        if (!isAllow)
            return;
        let timer = setTimeout(() => {
            console.log("throttle");
            clearTimeout(timer);
            timer = null;
            isAllow = true;
        },1000);
    };
    fun();
}

二者的區(qū)別是后者使用了isAllow標志位來判斷是否需要執(zhí)行函數(shù)狈癞。

  • 滾動條實例
    一個十分常見的例子茄靠,當用戶的鼠標滾動的時候,檢查鼠標位置離底部的距離蝶桶,當接近底部時慨绳,發(fā)起Ajax請求。
    https://codepen.io/dcorb/pen/eJLMxa

requestAnimationFrame

requestAnimationFrame是另一種限制函數(shù)執(zhí)行次數(shù)的方式真竖,可以認為與_.throttle類似脐雪,但是其擁有更高的準確度,因為其本身就是為了更好的精確度而生的原生API恢共。

requestAnimationFrame的優(yōu)點

  • 致力于60fps(16ms每幀)战秋,自己可以決定最好的渲染時間。
  • 簡單且標準的原生API讨韭,不太容易改變脂信,減少維護成本。

requestAnimationFrame的缺點

  • 需要手動開始或取消rAFs拐袜,.debounce.throttle則不需要吉嚣。
  • 如果tab沒有激活梢薪,則不會執(zhí)行蹬铺,即使觸發(fā) Ascroll, mouse 或者 keyboard 等事件也不行.
  • 不支持 IE9, Opera Mini 和 老版本 Android.
  • 不支持node.js,所以不能在服務端使用秉撇。

requestAnimationFrame實例

_.throttle相比甜攀,同時設置 16ms, 相同的性能環(huán)境下琐馆,rAF 可以在更復雜的情況下?lián)碛懈玫慕Y果规阀。

https://codepen.io/dcorb/pen/pgOKKw

四、結論

  • 一般來說瘦麸,如果你的JavaScript函數(shù)是直接繪制或者動畫某些屬性谁撼,那么可以使用requestAnimationFrame,在需要重新計算元素位置的地方使用滋饲。

  • 需要發(fā)起Ajax請求厉碟,或者決定是否增加或刪除一個類的時候喊巍,可以考慮使用_.debounce 或者_.throttle

  • debounce: 在事件被觸發(fā)x秒后再執(zhí)行回調箍鼓,如果在這x秒內又被觸發(fā)崭参,則重新計時。

  • throttle: 規(guī)定在一個單位時間內款咖,只能觸發(fā)一次函數(shù)何暮。如果這個單位時間內觸發(fā)多次函數(shù),只有一次生效铐殃。

  • requestAnimationFrame: 節(jié)流的另外一種選項海洼,在函數(shù)重新計算或者渲染元素時,需要平滑過渡或者動畫的時候使用富腊。

五贰军、參考

https://css-tricks.com/debouncing-throttling-explained-examples/

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蟹肘,隨后出現(xiàn)的幾起案子词疼,更是在濱河造成了極大的恐慌,老刑警劉巖帘腹,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贰盗,死亡現(xiàn)場離奇詭異,居然都是意外死亡阳欲,警方通過查閱死者的電腦和手機舵盈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來球化,“玉大人秽晚,你說我怎么就攤上這事⊥灿蓿” “怎么了赴蝇?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長巢掺。 經(jīng)常有香客問我句伶,道長,這世上最難降的妖魔是什么陆淀? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任考余,我火速辦了婚禮,結果婚禮上轧苫,老公的妹妹穿的比我還像新娘楚堤。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布身冬。 她就那樣靜靜地躺著鳄袍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吏恭。 梳的紋絲不亂的頭發(fā)上拗小,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音樱哼,去河邊找鬼哀九。 笑死,一個胖子當著我的面吹牛搅幅,可吹牛的內容都是我干的阅束。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼茄唐,長吁一口氣:“原來是場噩夢啊……” “哼息裸!你這毒婦竟也來了?” 一聲冷哼從身側響起沪编,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤呼盆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蚁廓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體访圃,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年相嵌,在試婚紗的時候發(fā)現(xiàn)自己被綠了腿时。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡饭宾,死狀恐怖批糟,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情看铆,我是刑警寧澤徽鼎,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站性湿,受9級特大地震影響纬傲,放射性物質發(fā)生泄漏满败。R本人自食惡果不足惜肤频,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望算墨。 院中可真熱鬧宵荒,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至暑刃,卻和暖如春厢漩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背岩臣。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工溜嗜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人架谎。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓炸宵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谷扣。 傳聞我的和親對象是個殘疾皇子土全,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容