underscore.js 防抖封裝

underscore.js 防抖設(shè)置

在實際的工作中,我們經(jīng)常會遇到限制客戶多次點擊,多次滑動而重復(fù)提交代碼的過程,為了能夠有效的防止這種情況,我們今天來討論應(yīng)該如何處理節(jié)流的控制。

前言

setTimeoutclearTimeout

// clearTimeout 雖然取消了定時器,但是timer并沒有取消,只是如果你沒有引用,就會被垃圾回收。
  let timer = setTimeout('console.log(11)')
  clearTimeout(timer)
  console.log(timer) // 20  一個數(shù)字

實例重現(xiàn)

我們現(xiàn)在用戶從屏幕的一端滑動另外一端,剛開始count為 1,但是最后為165,就是說,如果這里面是一個復(fù)雜的ajax請求,用戶在短短幾秒的過程中,請求了接口165次,假如每次接口的返回的時間是300ms,想想,結(jié)果是什么,結(jié)果就是用戶會被卡死,作為一個好的開發(fā)人員,我們是不是應(yīng)該阻止這樣的情況發(fā)生?

  <style>
    .container {
      background-color: black;
      color: white;
      padding: 100px 0;
      text-align: center;
    }
  </style>
</head>

<body>
  <div class="container"></div>
  <script>
    let count = 1
    const container = document.querySelector('.container');
    function getUserAction() {
      container.innerHTML = count++;
      return 'getUserAction'
    };
    
    container.addEventListener(mousemove, getUserAction)
  </script>
</body>

解決這樣的方式一般有二種情況

  1. 防抖控制debounce
  2. 節(jié)流控制throttle

防抖控制

上面說了,主要就是二種情況,我們今天來討論下防抖控制

原理:在一段時間內(nèi)(n秒),不管用戶怎么點擊,我都不會觸發(fā),只有等到n秒后才會執(zhí)行,如果中途n秒內(nèi),用戶又再次點擊,那我就以用戶新點擊的時間開始重新計算,n秒后才執(zhí)行??傊壕褪窃谟脩粲|發(fā)完事件后,n秒內(nèi)不再觸發(fā),我才執(zhí)行事件,我就是任性!??!

防抖第一版

我們根據(jù)原理可以實現(xiàn)一個函數(shù):

function debounce(func, wait) {
  let timer
  return function() {
    if(timer) clearTimeout(timer)
    timer = setTimeout(func, wait)
  }
}
container.addEventListener(mousemove, debounce(getUserAction, 1000))

這樣處理后,在一秒內(nèi),用戶重新觸發(fā)事件的話,都不會執(zhí)行,只有在最后一次觸發(fā)1s后才會觸發(fā)事件。

防抖第二版

研究一個函數(shù)的時候,我們都知道參數(shù)this,很重要,所有這里我們也需要處理getUserAction函數(shù)內(nèi)部的this和參數(shù)。

如果我們按照第一版不處理的話,getUserAction內(nèi)部的this就是window了,但是根據(jù)事件觸發(fā)的this規(guī)則,this應(yīng)該指向事件觸發(fā)的dom元素。

加入this:

function debounce(func, wait) {
  let timer, context
  return function() {
    context = this
    if(timer) clearTimeout(timer)
    timer = setTimeout(() => {
      func.apply(context)
    }, wait)
  }
}
container.addEventListener(mousemove, debounce(getUserAction, 1000))

處理完this后,是不是要思考每一個事件處理都是有一個事件參數(shù)e,我們也需要把這個參數(shù)e傳入到getUserAction內(nèi)部,方便我們處理。

處理參數(shù):

function debounce(func, wait) {
  let timer, context
  return function(...args) {
    context = this
    if(timer) clearTimeout(timer)
    timer = setTimeout(() => {
      func.apply(context,args)
    }, wait)
  }
}
container.addEventListener(mousemove, debounce(getUserAction, 1000))

防抖第三版

突然老板來了一個新需求,我不想等n秒后執(zhí)行,我想立刻執(zhí)行事件觸發(fā),但是n秒內(nèi)不再觸發(fā),自己想想好像也是合理的需求。

所有我們給debounce添加一個參數(shù)來控制是立即執(zhí)行還是n秒后再執(zhí)行

function debounce(func, wait, immediate) {
  let timer, context
  return function(...args) {
    context = this
    if(timer) clearTimeout(timer)
    if(immediate) {
      // 控制是否已經(jīng)執(zhí)行過
      call = !timer
      timer = setTimeout(() => {
        timer = null
      },wait)
      if(call) {
        func.apply(this,args)
      } 
    } else {
      timer = setTimeout(() => {
        func.apply(context,args)
      }, wait)
    }
  }
}

這樣處理后,我們就可以根據(jù)immediate的值,來用2種方法來執(zhí)行。

防抖第四版

每一個函數(shù)都有一個返回值,getUserAction的返回值如果需要被利用呢?當不是直接執(zhí)行的時候(immediate=false)討論返回值沒有意義,一直都是undefined(異步),所以只有立即執(zhí)行的時候,才有返回值。

function debounce(func, wait, immediate) {
  let timer, context, result
  return function(...args) {
    context = this
    if(timer) clearTimeout(timer)
    if(immediate) {
      // 控制是否已經(jīng)執(zhí)行過
      call = !timer
      timer = setTimeout(() => {
        timer = null
      },wait)
      if(call) {
        result = func.apply(this,args)
      } 
    } else {
      timer = setTimeout(() => {
        func.apply(context,args)
      }, wait)
    }
    return result
  }
}

做到這里,一個基本的防抖封裝已經(jīng)完成了,一大部分的情況都可以處理了,但是有沒有想到過一種情況,現(xiàn)實工作中,就是讓用戶點擊一個按鈕,取消等待,繼續(xù)觸發(fā)函數(shù)?

防抖第五版

最后我們再思考一個小需求,我希望能取消 debounce 函數(shù),比如說我 debounce 的時間間隔是 10 秒鐘,immediate 為 true,這樣的話,我只有等 10 秒后才能重新觸發(fā)事件,現(xiàn)在我希望有一個按鈕,點擊后,取消防抖,這樣我再去觸發(fā),就可以又立刻執(zhí)行啦,是不是很開心?

function debounce(func, wait, immediately) {
  let timer
  let debounced =  function (...args) {
    let result
    // 清除鬧鐘后,鬧鐘還是存在的
    if (timer) clearTimeout(timer)
    if (immediately) {
      let called = !timer
      timer = setTimeout(() => {
        timer = null
      }, wait)
      if (called) {
        result = func.apply(this,args)
      }
    } else {
      timer = setTimeout(() => {
        func.apply(this, args)
      }, wait)
    }
    return result
  }
  debounced.cancel = function() {
    clearTimeout(timer)
    timer = null
  }
  return debounced
}

那么如何使用呢?

let action = debounce(getUserAction, 100000, true)
container.addEventListener('mousemove', action)
btn.addEventListener('click', action.cancel)

恭喜你,完成了一個防抖的封裝。


源碼地址

點擊這里

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,765評論 25 709
  • 夜星墮,旭日開,山河斗轉(zhuǎn),人世飄零,朝起暮往急匆匆。 春風起,楊柳依,秋霜急雪,四季幻滅,寒來暑往歲相推。 局中人...
    想cx閱讀 45評論 1 4
  • 越圖越毀圖
    水泉印閱讀 268評論 0 1
  • nth-child(1)等于first-childnth-of-type(1)等于first-of-type如圖所...
    我的昵稱去哪了閱讀 590評論 0 0

友情鏈接更多精彩內(nèi)容