亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

throttle函數(shù)與debounce函數(shù)

Prasanta / 666人閱讀

摘要:當(dāng)函數(shù)被再次觸發(fā)時(shí),清除已設(shè)置的定時(shí)器,重新設(shè)置定時(shí)器。函數(shù)設(shè)置定時(shí)器,并根據(jù)傳參配置決定是否在等待開始時(shí)執(zhí)行函數(shù)。函數(shù)取消定時(shí)器,并重置內(nèi)部參數(shù)。

throttle函數(shù)與debounce函數(shù)

有時(shí)候,我們會(huì)對(duì)一些觸發(fā)頻率較高的事件進(jìn)行監(jiān)聽,如果在回調(diào)里執(zhí)行高性能消耗的操作,反復(fù)觸發(fā)時(shí)會(huì)使得性能消耗提高,瀏覽器卡頓,用戶使用體驗(yàn)差?;蛘呶覀冃枰獙?duì)觸發(fā)的事件延遲執(zhí)行回調(diào),此時(shí)可以借助throttle/debounce函數(shù)來實(shí)現(xiàn)需求。

throttle函數(shù)

throttle函數(shù)用于限制函數(shù)觸發(fā)的頻率,每個(gè)delay時(shí)間間隔,最多只能執(zhí)行函數(shù)一次。一個(gè)最常見的例子是在監(jiān)聽resize/scroll事件時(shí),為了性能考慮,需要限制回調(diào)執(zhí)行的頻率,此時(shí)便會(huì)使用throttle函數(shù)進(jìn)行限制。

由throttle函數(shù)的定義可知,每個(gè)delay時(shí)間間隔,最多只能執(zhí)行函數(shù)一次,所以需要有一個(gè)變量來記錄上一個(gè)執(zhí)行函數(shù)的時(shí)刻,再結(jié)合延遲時(shí)間和當(dāng)前觸發(fā)函數(shù)的時(shí)刻來判斷當(dāng)前是否可以執(zhí)行函數(shù)。在設(shè)定的時(shí)間間隔內(nèi),函數(shù)最多只能被執(zhí)行一次。同時(shí),第一次觸發(fā)時(shí)立即執(zhí)行函數(shù)。以下為throttle實(shí)現(xiàn)的簡略代碼:

function throttle(fn, delay) {
    var timer;
    return function() {
        var last = timer;
        var now = Date.now();
        if(!last) {
          timer = now;
          fn.apply(this,arguments);
          return;
        }
        if(last + delay > now) return;
        timer = now;
        fn.apply(this,arguments);
    }
}
debounce函數(shù)

debounce函數(shù)同樣可以減少函數(shù)觸發(fā)的頻率,但限制的方式有點(diǎn)不同。當(dāng)函數(shù)觸發(fā)時(shí),使用一個(gè)定時(shí)器延遲執(zhí)行操作。當(dāng)函數(shù)被再次觸發(fā)時(shí),清除已設(shè)置的定時(shí)器,重新設(shè)置定時(shí)器。如果上一次的延遲操作還未執(zhí)行,則會(huì)被清除。一個(gè)最常見的業(yè)務(wù)場景是監(jiān)聽onchange事件,根據(jù)用戶輸入進(jìn)行搜索,獲取遠(yuǎn)程數(shù)據(jù)。為避免多次ajax請(qǐng)求,使用debounce函數(shù)作為onchange的回調(diào)。

由debounce的用途可知,實(shí)現(xiàn)延遲回調(diào)需要用到setTimeout設(shè)置定時(shí)器,每次重新觸發(fā)時(shí)需要清除原來的定時(shí)器并重新設(shè)置,簡單的代碼實(shí)現(xiàn)如下:

function debounce(fn, delay){
    var timer;
    return function(){
        if(timer) clearTimeout(timer)
        timer = setTimeout(()=>{
            timer = undefined
            fn.apply(this, arguments);
        }, delay||0)
    }
}
小結(jié)

throttle函數(shù)與debounce函數(shù)的區(qū)別就是throttle函數(shù)在觸發(fā)后會(huì)馬上執(zhí)行,而debounce函數(shù)會(huì)在一定延遲后才執(zhí)行。從觸發(fā)開始到延遲結(jié)束,只執(zhí)行函數(shù)一次。上文中throttle函數(shù)實(shí)現(xiàn)并未使用定時(shí)器,開源類庫提供的throttle方法大多使用定時(shí)器實(shí)現(xiàn),而且開源通過參數(shù)配置項(xiàng),區(qū)分throttle函數(shù)與debounce函數(shù)。

實(shí)現(xiàn)throttle和debounce的開源庫

上文中實(shí)現(xiàn)的代碼較為簡單,未考慮參數(shù)類型的判斷及配置、測試等。下面介紹部分實(shí)現(xiàn)throttle和debounce的開源的類庫。

jQuery.throttle jQuery.debounce

$.throttle指向函數(shù)jq_throttle。jq_throttle接收四個(gè)參數(shù) delay, no_trailing, callback, debounce_mode。參數(shù)二no_trailing在throttle模式中指示。除了在文檔上說明的三個(gè)參數(shù)外,第四個(gè)參數(shù)debounce_mode用于指明是否是debounce模式,真即debounce模式,否則是throttle模式。

jq_throttle函數(shù)內(nèi),先聲明需要使用的變量timeout_id(定時(shí)器)和last_exec(上一次執(zhí)行操作的時(shí)間),進(jìn)行了參數(shù)判斷和交換,然后定義了內(nèi)部函數(shù)wrapper,作為返回的函數(shù)。

wrapper內(nèi),有用于更新上次執(zhí)行操作的時(shí)刻并執(zhí)行真正的操作的函數(shù)exec,用于清除debounce模式中定時(shí)器的函數(shù)clear,保存當(dāng)前觸發(fā)時(shí)刻和上一次執(zhí)行操作時(shí)刻的時(shí)間間隔的變量elapsed。

如果是debounce模式且timeout_id空,執(zhí)行exec。如果定時(shí)器timeout_id存在則清除定時(shí)器。

如果是throttle模式且elapsed大于延遲時(shí)間delay,執(zhí)行exec;否則,當(dāng)no_trainling非真時(shí),更新timeout_id,重新設(shè)置定時(shí)器,補(bǔ)充在上面清除的定時(shí)器:如果是debounce模式,執(zhí)行timeout_id = setTimeout(clear, delay),如果是throttle模式,執(zhí)行timeout_id = setTimeout(exec, delay - elapsed)。

 $.throttle = jq_throttle = function( delay, no_trailing, callback, debounce_mode ) {
    // After wrapper has stopped being called, this timeout ensures that
    // `callback` is executed at the proper times in `throttle` and `end`
    // debounce modes.
    var timeout_id,
      
      // Keep track of the last time `callback` was executed.
      last_exec = 0;
    
    // `no_trailing` defaults to falsy.
    if ( typeof no_trailing !== "boolean" ) {
      debounce_mode = callback;
      callback = no_trailing;
      no_trailing = undefined;
    }
    
    // The `wrapper` function encapsulates all of the throttling / debouncing
    // functionality and when executed will limit the rate at which `callback`
    // is executed.
    function wrapper() {
      var that = this,
        elapsed = +new Date() - last_exec,
        args = arguments;
      
      // Execute `callback` and update the `last_exec` timestamp.
      function exec() {
        last_exec = +new Date();
        callback.apply( that, args );
      };
      
      // If `debounce_mode` is true (at_begin) this is used to clear the flag
      // to allow future `callback` executions.
      function clear() {
        timeout_id = undefined;
      };
      
      if ( debounce_mode && !timeout_id ) {
        // Since `wrapper` is being called for the first time and
        // `debounce_mode` is true (at_begin), execute `callback`.
        exec();
      }
      
      // Clear any existing timeout.
      timeout_id && clearTimeout( timeout_id );
      
      if ( debounce_mode === undefined && elapsed > delay ) {
        // In throttle mode, if `delay` time has been exceeded, execute
        // `callback`.
        exec();
        
      } else if ( no_trailing !== true ) {
        // In trailing throttle mode, since `delay` time has not been
        // exceeded, schedule `callback` to execute `delay` ms after most
        // recent execution.
        // 
        // If `debounce_mode` is true (at_begin), schedule `clear` to execute
        // after `delay` ms.
        // 
        // If `debounce_mode` is false (at end), schedule `callback` to
        // execute after `delay` ms.
        timeout_id = setTimeout( debounce_mode ? clear : exec, debounce_mode === undefined ? delay - elapsed : delay );
      }
    };
    
    // Set the guid of `wrapper` function to the same of original callback, so
    // it can be removed in jQuery 1.4+ .unbind or .die by using the original
    // callback as a reference.
    if ( $.guid ) {
      wrapper.guid = callback.guid = callback.guid || $.guid++;
    }
    
    // Return the wrapper function.
    return wrapper;
  };

debounce函數(shù)內(nèi)部實(shí)際調(diào)用了throttle函數(shù)。

  $.debounce = function( delay, at_begin, callback ) {
    return callback === undefined
      ? jq_throttle( delay, at_begin, false )
      : jq_throttle( delay, callback, at_begin !== false );
  };
lodash的throttle與debounce

lodash中相比jQuery,提供了leadingtrailing選項(xiàng),表示在函數(shù)在等待開始時(shí)被執(zhí)行和函數(shù)在等待結(jié)束時(shí)被執(zhí)行。而對(duì)于debounce函數(shù),還提供了maxWait,當(dāng)debounce函數(shù)重復(fù)觸發(fā)時(shí),有可能由于wait過長,回調(diào)函數(shù)沒機(jī)會(huì)執(zhí)行,maxWait字段確保了當(dāng)函數(shù)重復(fù)觸發(fā)時(shí),每maxWait毫秒執(zhí)行函數(shù)一次。

maxWait的作用,我們可以聯(lián)想到,提供maxWait的debounce函數(shù)與throttle函數(shù)的作用是一樣的;事實(shí)上,lodash的throttle函數(shù)就是指明maxWait的debounce函數(shù)。

lodash重新設(shè)置計(jì)時(shí)器時(shí),并沒有調(diào)用clearTimeout清除定時(shí)器,而是在執(zhí)行回調(diào)前判斷參數(shù)和執(zhí)行上下文是否存在,存在時(shí)則執(zhí)行回調(diào),執(zhí)行完之后將參數(shù)和上下文賦值為undefined;重復(fù)觸發(fā)時(shí),參數(shù)和上下文為空,不執(zhí)行函數(shù)。這也是與jQuery實(shí)現(xiàn)的不同之一

以下為debounce函數(shù)內(nèi)的函數(shù)和變量:

局部變量lastInvokeTime記錄上次執(zhí)行時(shí)間,默認(rèn)0。

函數(shù)invokeFunc執(zhí)行回調(diào)操作,并更新上一次執(zhí)行時(shí)間lastInvokeTime。

函數(shù)leadingEdge設(shè)置定時(shí)器,并根據(jù)傳參配置決定是否在等待開始時(shí)執(zhí)行函數(shù)。

函數(shù)shouldInvoke判斷是否可以執(zhí)行回調(diào)函數(shù)。

函數(shù)timerExpired判斷是否可以立即執(zhí)行函數(shù),如果可以則執(zhí)行,否則重新設(shè)置定時(shí)器,函數(shù)remainingWait根據(jù)上次觸發(fā)時(shí)間/執(zhí)行時(shí)間和當(dāng)前時(shí)間返回重新設(shè)置的定時(shí)器的時(shí)間間隔。

函數(shù)trailingEdge根據(jù)配置決定是否執(zhí)行函數(shù),并清空timerId。

函數(shù)cancel取消定時(shí)器,并重置內(nèi)部參數(shù)。函數(shù)debounced是返回的內(nèi)部函數(shù)。

debounced內(nèi)部先獲取當(dāng)前時(shí)間time,判斷是否能執(zhí)行函數(shù)。如果可以執(zhí)行,且timerId空,表示可以馬上執(zhí)行函數(shù)(說明是第一次觸發(fā)或已經(jīng)執(zhí)行過trailingEdge),執(zhí)行leadingEdge,設(shè)置定時(shí)器。

如果timerId非空且傳參選項(xiàng)有maxWait,說明是throttle函數(shù),設(shè)置定時(shí)器延遲執(zhí)行timerExpired并立即執(zhí)行invokeFunc,此時(shí)在timerExpired中設(shè)置的定時(shí)器的延遲執(zhí)行時(shí)間是wait - timeSinceLastCallmaxWait - timeSinceLastInvoke的最小值,分別表示通過wait設(shè)置的仍需等待執(zhí)行函數(shù)的時(shí)間(下一次trailing的時(shí)間)和通過maxWait設(shè)置的仍需等待執(zhí)行函數(shù)的時(shí)間(下一次maxing的時(shí)間)。

function debounce(func, wait, options) {
      var lastArgs,
          lastThis,
          maxWait,
          result,
          timerId,
          lastCallTime,
          lastInvokeTime = 0,
          leading = false,
          maxing = false,
          trailing = true;

      if (typeof func != "function") {
        throw new TypeError(FUNC_ERROR_TEXT);
      }
      wait = toNumber(wait) || 0;
      if (isObject(options)) {
        leading = !!options.leading;
        maxing = "maxWait" in options;
        maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
        trailing = "trailing" in options ? !!options.trailing : trailing;
      }

      function invokeFunc(time) {
        var args = lastArgs,
            thisArg = lastThis;

        lastArgs = lastThis = undefined;
        lastInvokeTime = time;
        result = func.apply(thisArg, args);
        return result;
      }

      function leadingEdge(time) {
        // Reset any `maxWait` timer.
        lastInvokeTime = time;
        // Start the timer for the trailing edge.
        timerId = setTimeout(timerExpired, wait);
        // Invoke the leading edge.
        return leading ? invokeFunc(time) : result;
      }

      function remainingWait(time) {
        var timeSinceLastCall = time - lastCallTime,
            timeSinceLastInvoke = time - lastInvokeTime,
            timeWaiting = wait - timeSinceLastCall;

        return maxing
          ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
          : timeWaiting;
      }

      function shouldInvoke(time) {
        var timeSinceLastCall = time - lastCallTime,
            timeSinceLastInvoke = time - lastInvokeTime;

        // Either this is the first call, activity has stopped and we"re at the
        // trailing edge, the system time has gone backwards and we"re treating
        // it as the trailing edge, or we"ve hit the `maxWait` limit.
        return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
          (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
      }

      function timerExpired() {
        var time = now();
        if (shouldInvoke(time)) {
          return trailingEdge(time);
        }
        // Restart the timer.
        timerId = setTimeout(timerExpired, remainingWait(time));
      }

      function trailingEdge(time) {
        timerId = undefined;

        // Only invoke if we have `lastArgs` which means `func` has been
        // debounced at least once.
        if (trailing && lastArgs) {
          return invokeFunc(time);
        }
        lastArgs = lastThis = undefined;
        return result;
      }

      function cancel() {
        if (timerId !== undefined) {
          clearTimeout(timerId);
        }
        lastInvokeTime = 0;
        lastArgs = lastCallTime = lastThis = timerId = undefined;
      }

      function flush() {
        return timerId === undefined ? result : trailingEdge(now());
      }

      function debounced() {
        var time = now(),
            isInvoking = shouldInvoke(time);

        lastArgs = arguments;
        lastThis = this;
        lastCallTime = time;

        if (isInvoking) {
          if (timerId === undefined) {
            return leadingEdge(lastCallTime);
          }
          if (maxing) {
            // Handle invocations in a tight loop.
            timerId = setTimeout(timerExpired, wait);
            return invokeFunc(lastCallTime);
          }
        }
        if (timerId === undefined) {
          timerId = setTimeout(timerExpired, wait);
        }
        return result;
      }
      debounced.cancel = cancel;
      debounced.flush = flush;
      return debounced;
    }

throttle函數(shù)則是設(shè)置了maxWait選項(xiàng)且leading為真的debounce函數(shù)。

function throttle(func, wait, options) {
      var leading = true,
          trailing = true;

      if (typeof func != "function") {
        throw new TypeError(FUNC_ERROR_TEXT);
      }
      if (isObject(options)) {
        leading = "leading" in options ? !!options.leading : leading;
        trailing = "trailing" in options ? !!options.trailing : trailing;
      }
      return debounce(func, wait, {
        "leading": leading,
        "maxWait": wait,
        "trailing": trailing
      });
    }
參考

Throttling and debouncing in JavaScript

Debouncing and Throttling Explained Through Examples

jquery-throttle-debounce源碼

_.debounce源碼

聊聊lodash的debounce實(shí)現(xiàn)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/108445.html

相關(guān)文章

  • 淺談throttle以及debounce的原理和實(shí)現(xiàn)

    摘要:淺談以及的原理和實(shí)現(xiàn)背景日常開發(fā)中我們經(jīng)常會(huì)遇到一些需要節(jié)流調(diào)用或者壓縮調(diào)用次數(shù)的情況例如之前我在完成一個(gè)需求的時(shí)候就遇到了因?yàn)楹蠖瞬l(fā)問題導(dǎo)致收到多條信息從而導(dǎo)致函數(shù)被重復(fù)調(diào)用的情況當(dāng)時(shí)的做法是通過對(duì)函數(shù)的調(diào)用進(jìn)行注冊(cè)遇到多次調(diào)用的時(shí)候清 淺談throttle以及debounce的原理和實(shí)現(xiàn) 背景 日常開發(fā)中,我們經(jīng)常會(huì)遇到一些需要節(jié)流調(diào)用,或者壓縮調(diào)用次數(shù)的情況,例如之前我在完成...

    jsbintask 評(píng)論0 收藏0
  • JS throttledebounce的區(qū)別

    摘要:可以看下面的栗子這個(gè)圖中圖中每個(gè)小格大約,右邊有原生事件與節(jié)流去抖插件的與事件。即如果有連續(xù)不斷的觸發(fā),每執(zhí)行一次,用在每隔一定間隔執(zhí)行回調(diào)的場景。執(zhí)行啦打印執(zhí)行啦打印執(zhí)行啦節(jié)流按照上面的說明,節(jié)流就是連續(xù)多次內(nèi)的操作按照指定的間隔來執(zhí)行。 一般在項(xiàng)目中我們會(huì)對(duì)input、scroll、resize等事件進(jìn)行節(jié)流控制,防止事件過多觸發(fā),減少資源消耗;在vue的官網(wǎng)的例子中就有關(guān)于lod...

    wawor4827 評(píng)論0 收藏0
  • Debounce vs Throttle

    摘要:那么還有最后一個(gè)問題,那我之前設(shè)置的定時(shí)器怎么辦呢定時(shí)器執(zhí)行的是這個(gè)函數(shù),而這個(gè)函數(shù)又會(huì)通過進(jìn)行一次判斷。 我們?cè)谔幚硎录臅r(shí)候,有些事件由于觸發(fā)太頻繁,而每次事件都處理的話,會(huì)消耗太多資源,導(dǎo)致瀏覽器崩潰。最常見的是我們?cè)谝苿?dòng)端實(shí)現(xiàn)無限加載的時(shí)候,移動(dòng)端本來滾動(dòng)就不是很靈敏,如果每次滾動(dòng)都處理的話,界面就直接卡死了。 因此,我們通常會(huì)選擇,不立即處理事件,而是在觸發(fā)一定次數(shù)或一定時(shí)間...

    xcold 評(píng)論0 收藏0
  • debouncing throttling

    摘要:一個(gè)使用場景某些瀏覽器事件可能會(huì)在短時(shí)間內(nèi)高頻觸發(fā),比如整窗口大小或滾動(dòng)頁面。這會(huì)導(dǎo)致非常嚴(yán)重的性能問題。實(shí)現(xiàn)與類似,接收兩個(gè)參數(shù),一個(gè)是需要截流的函數(shù),另一個(gè)是函數(shù)執(zhí)行間隔閾值。 一個(gè)使用場景:某些瀏覽器事件可能會(huì)在短時(shí)間內(nèi)高頻觸發(fā),比如:整窗口大小或滾動(dòng)頁面。如果給窗口滾動(dòng)事件添加一個(gè)事件監(jiān)聽器,然后用戶不停地快速滾動(dòng)頁面,那你的事件可能在短短數(shù)秒之內(nèi)被觸發(fā)數(shù)千次。這會(huì)導(dǎo)致非常嚴(yán)重...

    zzir 評(píng)論0 收藏0
  • [譯]通過實(shí)例講解Debouncing和Throtting(防抖節(jié)流)

    摘要:譯通過實(shí)例講解和防抖與節(jié)流源碼中推薦的文章,為了學(xué)習(xí)英語,翻譯了一下原文鏈接作者本文來自一位倫敦前端工程師的技術(shù)投稿。首次或立即你可能發(fā)現(xiàn)防抖事件在等待觸發(fā)事件執(zhí)行,直到事件都結(jié)束后它才執(zhí)行。 [譯]通過實(shí)例講解Debouncing和Throtting(防抖與節(jié)流) lodash源碼中推薦的文章,為了學(xué)習(xí)(英語),翻譯了一下~ 原文鏈接 作者:DAVID CORBACHO 本文來自一位...

    Jenny_Tong 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<