zoukankan      html  css  js  c++  java
  • throttle與debounce算法的邏輯

    throttledebounce,是兩個算法。兩者處理的對象,都是那些需要被反復調用的函數,特別是回調函數。其目的,都是控制函數重複執行的頻率,避免性能下降。
    throttle的應用場景如瀏覽器窗口的resize,debounce的應用場景如搜索框的suggestion。
    throttle的基本思想很簡單,通過封裝(包裹),使原函數相鄰兩次執行時間必須大於一個值interval。如果第二次調用函數的時機,沒有超出interval,則忽略之,或者用setTimeout使函數在超出interval的某個時機再執行原函數
    debounce的思路也很簡單,通過封裝(包裹),使函數第一次調用時,不馬上執行原函數,而是設置一個timeout。如果在timeout等待過程中,繼續調用這個函數,則取消現在的timeout,并重新設置一個。
    但是underscore庫中的這兩個函數,加了一些參數,使得邏輯非常複雜。即使每個函數的核心代碼就20行左右,我也要花兩三天時間才能理順。

    throttle

      _.throttle = function(func, wait, options) {
        var context, args, result;
        var timeout = null;
        var previous = 0;
        if (!options) options = {};
        var later = function() {
          previous = options.leading === false ? 0 : _.now();
          timeout = null;
          result = func.apply(context, args);
          if (!timeout) context = args = null;
        };
        return function() {
          var now = _.now();
          if (!previous && options.leading === false) previous = now;
          var remaining = wait - (now - previous);
          context = this;
          args = arguments;
          //remaining > wait,在人為將系統時間調慢的情況下,成立
          if (remaining <= 0 || remaining > wait) {
            //由於setTimeout存在最小時間精度問題,因此有可能wait階段已過,但之前設置的setTimeout操作還未執行
            clearTimeout(timeout);
            timeout = null;
            previous = now;
            result = func.apply(context, args);
            if (!timeout) context = args = null;
          } else if (!timeout && options.trailing !== false) {
            timeout = setTimeout(later, remaining);
          }
          return result;
        };
      };
    

    該函數的第三個參數options,有兩個屬性leadingtrailing,都是布爾值。他們能形成四種組合形式:

    1. {"trailing": true,"leading": true},是為默認值
    2. {"trailing": true,"leading": false}
    3. {"trailing": false,"leading": true}
    4. {"trailing": false,"leading": false}

    這個參數會影響程序的流程,現以流程圖分述之。
    下為{"trailing": true,"leading": true}的情況:
    true/true
    下為{"trailing": true,"leading": false}的情況:
    true/false
    下為{"trailing": false,"leading": true}的情況:
    false,true
    下為{"trailing": false,"leading": false}的情況:
    false,false
    leadingtrailing這兩個科技術語,一般是用來表示「首」和「尾」。但是通過上面流程圖的演示,可知在這段程序中,他們各自代表的意義,並非典型的前與後的對立關係。外國程序員在用詞方面的瑕疵,也給我們的閱讀造成了一定的困擾。

    debounce

    _.debounce = function(func, wait, immediate) {// immediate默認為false
        var timeout, args, context, timestamp, result;
        var later = function() {
            var last = _.now() - timestamp;//如果在wait期間,函數被調用,導致timestamp被更新,則原函數始終不會被執行,直到函數停止被調用
            if (last < wait && last >= 0) {//系統時間調慢之後,則last < 0
                timeout = setTimeout(later, wait - last);
            } else {
                timeout = null;
                if (!immediate) {
                    result = func.apply(context, args);
                    if (!timeout) context = args = null;
                }
            }
        };
    
        return function() {
            context = this;
            args = arguments;
            timestamp = _.now();//此處是重點,每次都更新timestamp,這個值會影響到後面later函數的執行
            var callNow = immediate && !timeout;
            if (!timeout) timeout = setTimeout(later, wait);
            if (callNow) {//第一次調用該函數,或者上一個timeout已經被執行完畢,且immediate為true,則立即執行func函數
                result = func.apply(context, args);
                context = args = null;
            }
            return result;
        };
    };
    

    immediate這個參數,目的在於,在不存在timeout的情況下,先同步執行一次原函數,然後再設置一個timeout
    以下是流程圖:
    debounce

    測試

    除了上面的源代碼,我也在自己的Javascript庫中實現了這兩個函數,沒有那麼多的參數,邏輯更加清楚。
    為了測試這兩個函數,我寫了一段簡單的代碼:

    var todo = function() {
        var now = new Date();
        console.log(now.getSeconds() + "," + now.getMilliseconds());
    };
    var callback = debounce(todo, 1230); //事件的響應函數,參數1230ms
    var interval = setInterval(function() {
        callback();
    }, 100); //事件每100ms觸發一次
    setTimeout(function() {
        clearTimeout(interval);
    }, 6 * 1000); //事件持續6s
    
  • 相关阅读:
    拖拽文件上传demo
    JS权限树数据处理demo
    某面试题 a==1&&a==2&&a==3 的解决方案以及涉及知识
    解决forEach函数中异步调用及Promise.all()的基础使用
    基于element ui的el-date-picker 日、周、月粒度切换时间选择器
    基于element ui的el-table-column行内编辑input/select封装
    JavaScript优化技术
    vue-canvas-poster生成海报 jsZip打包称压缩包
    uniapp中使用uView时 TypeError: Cannot read property ‘mark‘ of undefined
    JS中深拷贝数组、对象、对象数组方法
  • 原文地址:https://www.cnblogs.com/jonkee/p/5124335.html
Copyright © 2011-2022 走看看