成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

Debounce vs Throttle

xcold / 2811人閱讀

摘要:那么還有最后一個問題,那我之前設(shè)置的定時器怎么辦呢定時器執(zhí)行的是這個函數(shù),而這個函數(shù)又會通過進行一次判斷。

我們在處理事件的時候,有些事件由于觸發(fā)太頻繁,而每次事件都處理的話,會消耗太多資源,導致瀏覽器崩潰。最常見的是我們在移動端實現(xiàn)無限加載的時候,移動端本來滾動就不是很靈敏,如果每次滾動都處理的話,界面就直接卡死了。

因此,我們通常會選擇,不立即處理事件,而是在觸發(fā)一定次數(shù)或一定時間之后進行處理。這時候我們有兩個選擇: debounce(防抖動)和 throttle(節(jié)流閥)。

之前看過很多文章都還是沒有太弄明白兩者之間的區(qū)別,最后通過看源碼大致了解了兩者之間的區(qū)別以及簡單的實現(xiàn)思路。

首先,我們通過實踐來最簡單的看看二者的區(qū)別:

可以看到,throttle會在第一次事件觸發(fā)的時候就執(zhí)行,然后每隔wait(我這里設(shè)置的2000ms)執(zhí)行一次,而debounce只會在事件結(jié)束之后執(zhí)行一次。

有了一個大概的印象之后,我們看一看lodash的源碼對debouncethrottle的區(qū)別。

這里討論默認情況

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

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

可以看到,throttle最后返回的還是debounce函數(shù),只是指定了options選項。那么接下來我們就集中分析debounce。

function debounce(fn, wait, options) {
    var lastArgs,
      lastThis,
          maxWait,
          result,
          timerId,
          lastCallTime,
          lastInvokeTime = 0,
          leading = false,
          maxing = false,
          trailing = true;
      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;
}

為了記錄每次執(zhí)行的相關(guān)信息,debounce函數(shù)最后返回的是一個函數(shù),形成一個閉包。

這也解釋了為什么這樣寫不行:

    window.addEventListener("resize", function(){
      _.debounce(onResize, 2000);
    });

這樣寫根本就不會調(diào)用內(nèi)部的debounced函數(shù)。

解決第一個不同

debounced內(nèi)部呢,首先記錄了當前調(diào)用的時間,然后通過shouldInvoke這個函數(shù)判斷是否應該調(diào)用傳入的func。

      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));
      }

可以看到,該函數(shù)返回true的幾個條件。其中需要我們引起注意的是最后一個條件,這是debouncethrottle的區(qū)別之一。

首先maxing通過函數(shù)開始的幾行代碼判斷:

      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;
      }

我們看到,在定義throttle的時候, 給debounce函數(shù)給傳入了options, 而里面包含maxWait這個屬性,因此,對于throttle來說,maxingtrue, 而沒有傳入optionsdebounce則為false。這就是二者區(qū)別之一。在這里決定了shouldInvoke函數(shù)返回的值,以及是否執(zhí)行接下去的邏輯判斷。

我們再回到debounced這個函數(shù):

  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);
        }

在第一次調(diào)用的時候,debouncethrottleisInvoking
true, 且此時timerId === undefined也成立,就返回leadingEdge(lastCallTime)這個函數(shù)。

那么我們再來看看leadingEdge 這個函數(shù);

      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;
      }

這里出現(xiàn)了debouncethrottle的第二個區(qū)別。這個函數(shù)首先是設(shè)置了一個定時器,隨后返回的結(jié)果由leading決定。在默認情況下,throttle傳入的leadingtrue,而debouncefalse。因此,throttle會馬上執(zhí)行傳入的函數(shù),而debounce不會。

這里我們就解決了它們的第一個不同:throttle會在第一次調(diào)用的時候就執(zhí)行,而debounce不會。

解決第二個不同

我們再回到shouldInvoke的返回條件那里,如果在一個時間內(nèi)頻繁的調(diào)用, 前面三個條件都不會成立,對于debounce來說,最后一個也不會成立。而對于throttle來說,首先maxingtrue, 而如果距離上一次*傳入的func 函數(shù)調(diào)用 大于maxWait最長等待時間的話,它也會返回true。

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

        return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
          (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
      }
        if (isInvoking) {
          if (timerId === undefined) {
            return leadingEdge(lastCallTime);
          }
          if (maxing) {
            // Handle invocations in a tight loop.
            timerId = setTimeout(timerExpired, wait);
            return invokeFunc(lastCallTime);
          }
        }

那么在shouldInvoke成立之后,throttle會設(shè)置一個定時器,返回執(zhí)行傳入函數(shù)的結(jié)果。

這就是debouncethrottle 之間的第二個區(qū)別:throttle會保證你每隔一段時間都會執(zhí)行,而debounce不會。

那么還有最后一個問題,那我之前設(shè)置的定時器怎么辦呢?

timerId = setTimeout(timerExpired, wait);

定時器執(zhí)行的是timerExpired這個函數(shù),而這個函數(shù)又會通過shouldInvoke進行一次判斷。

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

最后,傳入的func怎么執(zhí)行的呢?下面這個函數(shù)實現(xiàn):

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

        lastArgs = lastThis = undefined;
        lastInvokeTime = time;
        result = func.apply(thisArg, args);
        return result;
      }
餓了么的簡單實現(xiàn)

在看餓了么的infinite scroll這個源碼的時候,看到了一個簡單版本的實現(xiàn):

var throttle = function (fn, delay) {
  var now, lastExec, timer, context, args; 

  var execute = function () {
    fn.apply(context, args);
    lastExec = now;
  };

  return function () {
    context = this;
    args = arguments;

    now = Date.now();

    if (timer) {
      clearTimeout(timer);
      timer = null;
    }

    if (lastExec) {
      var diff = delay - (now - lastExec);
      if (diff < 0) {
        execute();
      } else {
        timer = setTimeout(() => {
          execute();
        }, diff);
      }
    } else {
      execute();
    }
  };
};

那么它的思路很簡單:

通過lastExec判斷是否是第一次調(diào)用,如果是,就馬上執(zhí)行處理函數(shù)。

隨后就會監(jiān)測,每次調(diào)用的時間與上次執(zhí)行函數(shù)的時間差,如果小于0,就立馬執(zhí)行。大于0就會在事件間隔之后執(zhí)行。

每次調(diào)用的時候都會清除掉上一次的定時任務(wù),這樣就會保證只有一個最近的定時任務(wù)在等待執(zhí)行。

那么它與lodash的一個最大的區(qū)別呢,就是它是關(guān)注與上次執(zhí)行處理函數(shù)的時間差, 而lodashshouldInvoke關(guān)注的是兩次事件調(diào)用函數(shù)的時間差

總結(jié)

總的來說,這種實現(xiàn)的主要部分呢,就是時間差定時器

最后,自己參照寫了簡單的debouncethrottle: Gist求指教!

參考資料

debouncing-throttling-explained-examples | CSS-Tricks

Lodash源碼

餓了么 vue-infinite-scroll

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

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/84048.html

相關(guān)文章

  • 一次發(fā)現(xiàn)underscore源碼bug的經(jīng)歷以及對學術(shù)界拿來主義的思考

    摘要:事情是如何發(fā)生的最近干了件事情,發(fā)現(xiàn)了源碼的一個。樓主找到的關(guān)于和區(qū)別的資料如下關(guān)于拿來主義為什么這么多文章里會出現(xiàn)澤卡斯的錯誤代碼樓主想到了一個詞,叫做拿來主義。的文章,就深刻抨擊了拿來主義這一現(xiàn)象。 事情是如何發(fā)生的 最近干了件事情,發(fā)現(xiàn)了 underscore 源碼的一個 bug。這件事本身并沒有什么可說的,但是過程值得我們深思,記錄如下,各位看官仁者見仁智者見智。 平時有瀏覽別...

    Lionad-Morotar 評論0 收藏0
  • throttledebounce的區(qū)別

    摘要:自己嘗試一下年在的文章中第一次看到的實現(xiàn)方法。這三種實現(xiàn)方法內(nèi)部不同,但是接口幾乎一致。如你所見,我們使用了參數(shù),因為我們只對用戶停止改變?yōu)g覽器大小時最后一次事件感興趣。 前幾天看到一篇文章,我的公眾號里也分享了《一次發(fā)現(xiàn)underscore源碼bug的經(jīng)歷以及對學術(shù)界拿來主義的思考》具體文章詳見,微信公眾號:showImg(https://segmentfault.com/img/b...

    Pluser 評論0 收藏0
  • 【譯】通過例子解釋 DebounceThrottle

    摘要:舉例舉例通過拖拽瀏覽器窗口,可以觸發(fā)很多次事件。不支持,所以不能在服務(wù)端用于文件系統(tǒng)事件。總結(jié)將一系列迅速觸發(fā)的事件例如敲擊鍵盤合并成一個單獨的事件。確保一個持續(xù)的操作流以每毫秒執(zhí)行一次的速度執(zhí)行。 Debounce 和 Throttle 是兩個很相似但是又不同的技術(shù),都可以控制一個函數(shù)在一段時間內(nèi)執(zhí)行的次數(shù)。 當我們在操作 DOM 事件的時候,為函數(shù)添加 debounce 或者 th...

    LeoHsiun 評論0 收藏0
  • JS進階篇3---函數(shù)“節(jié)流” VS “防抖”

    摘要:目的都是為了降低回調(diào)函數(shù)執(zhí)行頻率,節(jié)省計算機資源,優(yōu)化性能,提升用戶體驗。函數(shù)防抖事件頻繁觸發(fā)的情況下,只有經(jīng)過足夠的空閑時間,才執(zhí)行代碼一次。 函數(shù)節(jié)流和函數(shù)防抖的對比分析 一、前言 前端開發(fā)中,函數(shù)節(jié)流(throttle) 和 函數(shù)防抖(debounce) 作為常用的性能優(yōu)化方法,兩者都是用于優(yōu)化高頻率執(zhí)行 js 代碼的手段,那具體它們有什么異同點呢?有對這兩個概念不太了解的小伙伴...

    hlcc 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<