亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JavaScript中防抖和節(jié)流的實戰(zhàn)應用記錄

 更新時間:2022年04月28日 09:28:17   作者:Jimmy  
防抖與節(jié)流都是用來限制用戶頻發(fā)觸發(fā)事件的機制,下面這篇文章主要給大家介紹了關于JavaScript中防抖和節(jié)流的實戰(zhàn)應用,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下

前言

你可能會遇到這種的情況,一個站點使用自動填充的文本框,內(nèi)容的拖拽,效果的滾動。那么,你遇到防抖和截流的概率還是很高的。為了使得這些操作,比如自動填充能夠順暢工作,你需要引入防抖和截流功能。

  • 防抖 -> Debounce
  • 節(jié)流 -> Throttle

為什么我們需要防抖/節(jié)流

開篇已經(jīng)簡單提了,debounce/throttle 能讓你的站點表現(xiàn)更優(yōu)異。它們工作原理是通過減少動作發(fā)起的次數(shù)。我們簡單舉個例子,自動填充文本框觸發(fā)接口請求,如下:

input.addEventListener("input", e => {
  fetch(`/api/getOptions?query=${e.target.value}`)
    .then(res => res.json())
    .then(data => setOptions(data))
})

??上面的事件監(jiān)測器,監(jiān)聽到輸入文本框發(fā)生更改,就基于文本框的內(nèi)容觸發(fā)一個查詢接口。這看起來還不錯,但是用戶輸入 Samantha 文字會發(fā)生什么?

當用戶輸入 S,事件監(jiān)測器觸發(fā)請求,并帶上選項 S。當此請求正在調(diào)用的時候,Sa 輸入內(nèi)容會再次被監(jiān)聽,我們將重新以 Sa 為選項內(nèi)容發(fā)起新的請求。以此類推,這種請求會持續(xù)到我們輸完 Samantha 的內(nèi)容。

這會在短時間內(nèi)發(fā)起 8 次請求,但是我們只關心最后一次請求。這意味著前 7 的接口請求都是不必要的,純屬浪費時間和金錢。

為了避免不必要的請求發(fā)生,我們就需要防抖和截流。

防抖

我們先來談下防抖,因為它是解決自動文本框類問題的理想解決方案。防抖的原理是延遲一段時間吊起我們的函數(shù)。如果在這個時間段沒有發(fā)生什么,函數(shù)正常進行,但是有內(nèi)容發(fā)生變更后的一段時間觸發(fā)函數(shù)。這就意味著,防抖函數(shù)只會在特定的時間之后被觸發(fā)。

在我們的例子中,我們假設延遲 1 秒觸發(fā)。也就是當用戶停止輸入內(nèi)容后 1 秒,接口強求被吊起。如果我們在 1 秒內(nèi)輸完 Samantha 內(nèi)容,請求查詢內(nèi)容就是 Samantha。下面我們看看怎么應用防抖。

function debounce(cb, delay = 250) {
  let timeout

  return (...args) => {
    clearTimeout(timeout)
    timeout = setTimeout(() => {
      cb(...args)
    }, delay)
  }
}

上面的防抖函數(shù)帶有 cb 回調(diào)函數(shù)參數(shù)和一個延時的參數(shù)。我們在 debound 函數(shù)后返回回調(diào)函數(shù),這種包裝的方式,保證過了 delay 秒之后,回調(diào)函數(shù)才會被調(diào)用。最后,我們在每次調(diào)用 debounce 函數(shù)時清楚現(xiàn)有的定時器,以確保我們在延遲完成之前調(diào)用 debouce 函數(shù),并重新計時。

這意味著如果用戶在 1 秒內(nèi),每隔 300毫秒觸發(fā)一次輸入,上面 debouce 函數(shù)如下方式工作。

// Type S - Start timer
// Type a - Restart timer
// Type m - Restart timer
// Type a - Restart timer
// Type n - Restart timer
// Wait 1 second
// Call debounced function with Saman
// Type t - Start timer
// No more typing
// Call debounced function with Samant

不知你注意到?jīng)]有,即使我們已經(jīng)輸入了 Saman 文案動作超過了一秒中,回調(diào)函數(shù)也不會調(diào)起,知道再過 1 秒鐘才被調(diào)用。現(xiàn)在,看我們怎么引用防抖函數(shù)。

const updateOptions = debounce(query => {
  fetch(`/api/getOptions?query=${query}`)
    .then(res => res.json())
    .then(data => setOptions(data))
}, 1000)

input.addEventListener("input", e => {
  updateOptions(e.target.value)
)}

我們把請求函數(shù)放到回調(diào)函數(shù)中。

防抖函數(shù)在自動填充的情形非常好用,你也可以使用在其他地方,你想將多個觸發(fā)請求變成一個觸發(fā),以緩解服務器的壓力。

節(jié)流

像防抖一樣,節(jié)流也是限制請求的多次發(fā)送;但是,不同的是,防抖是每隔指定的時間發(fā)起請求。舉個例子,如果你在 throttle 函數(shù)中設置延遲時間是 1 秒,函數(shù)被調(diào)用執(zhí)行,用戶輸入每隔 1秒發(fā)起請求。看下下面的應用,你就明白了。

function throttle(cb, delay = 250) {
  let shouldWait = false

  return (...args) => {
    if (shouldWait) return

    cb(...args)
    shouldWait = true
    setTimeout(() => {
      shouldWait = false
    }, delay)
  }
}

debounce 和 throttle 函數(shù)都有一樣的參數(shù),但是它們主要的不同是,throttle 中的回調(diào)函數(shù)在函數(shù)執(zhí)行后立馬被調(diào)用,并且回調(diào)函數(shù)不在定時器函數(shù)內(nèi)?;卣{(diào)函數(shù)要做的唯一事情就是將 shouldWait 標識設置為 false。當我們第一次調(diào)用 throttle 函數(shù),會將 shouldWait 標識設置為 true。這延時的時間內(nèi)再次調(diào)用 throttle 函數(shù),那就什么都不做。當時間超出了延時的時間,shouldWait 標識才會設置為 false。

假設我們每隔 300 毫秒輸入一個字符,然后我們的延時是 1 秒。那么 throttle 函數(shù)會像下面這樣工作:

// Type S - Call throttled function with S
// Type a - Do nothing: 700ms left to wait
// Type m - Do nothing: 400ms left to wait
// Type a - Do nothing: 100ms left to wait
// Delay is over - Nothing happens
// Type n - Call throttled function with Saman
// No more typing
// Delay is over - Nothing happens

如果你留意看,你會發(fā)現(xiàn)第 1200 毫秒的時候,第二次 throttle 函數(shù)才會被觸發(fā)。已經(jīng)延遲了我們預設時間 200 毫秒。對于節(jié)流的需求來說,目前的 throttle 函數(shù)已經(jīng)滿足了需求。但是我們做些優(yōu)化,一旦 throttle 函數(shù)中的延時結束,我們就調(diào)用函數(shù)的前一個迭代。我們像下面這樣子應用。

function throttle(cb, delay = 1000) {
  let shouldWait = false
  let waitingArgs
  const timeoutFunc = () => {
    if (waitingArgs == null) {
      shouldWait = false
    } else {
      cb(...waitingArgs)
      waitingArgs = null
      setTimeout(timeoutFunc, delay)
    }
  }

  return (...args) => {
    if (shouldWait) {
      waitingArgs = args
      return
    }

    cb(...args)
    shouldWait = true
    setTimeout(timeoutFunc, delay)
  }
}

上面的代碼有點嚇人,但是原理都一樣。不同的是,在 throttle 函數(shù)延時時,后者存儲了前一個 args 參數(shù)值作為變量 waitingArgs。當延遲完成后,我們會檢查 waitingArgs 是否有內(nèi)容。如果沒有內(nèi)容,我們會將 shouldWait 設置為 false,然后進入下一次觸發(fā)。如果 waitingArgs 有內(nèi)容,這就意味著延時到了之后,我們將會帶上 waitingArgs 參數(shù)觸發(fā)我們的回調(diào)函數(shù),然后重置我們的定時器。

這個版本的 throttle 函數(shù)也是延時時間為 1 秒,每隔 300 毫秒輸入值,效果如下:

// Type S - Call throttled function with S
// Type a - Save Sa to waiting args: 700ms left to wait
// Type m - Save Sam to waiting args: 400ms left to wait
// Type a - Save Sama to waiting args: 100ms left to wait
// Delay is over - Call throttled function with Sama
// Type n - Save Saman to waiting args: 700ms left to wait
// No more typing
// Delay is over - Call throttled function with Saman

正如你所看到的,每次我們觸發(fā) throttle 函數(shù)時,如果延時時間結束,我們要么調(diào)用回調(diào)函數(shù),要么保存要在延時結束時使用的參數(shù)。如果這個參數(shù)有值的話,當延時結束時,我們將使用它。這就保證了 throttle 函數(shù)在延時結束時獲取到最新的參數(shù)值。

我們看下怎么應用到我們的例子中。

const updateOptions = throttle(query => {
  fetch(`/api/getOptions?query=${query}`)
    .then(res => res.json())
    .then(data => setOptions(data))
}, 500)

input.addEventListener("input", e => {
  updateOptions(e.target.value)
)}

你會發(fā)現(xiàn),我們的應用跟 debounce 函數(shù)很相似,除了將 debounce 名改為 throttle。

當然,自動填充文本內(nèi)容例子,對 throttle 函數(shù)并不適用,但是,如果你處理類如更改元素大小,元素拖拉拽,或者其他多次發(fā)生的事件,那么 throttle 函數(shù)是理想的選擇。因為 throttle 每次延時結束時,你都會獲得有關事件的更新信息,而 debounce 需要等待輸入后延時后才能觸發(fā)。總的來說,當你想定期將多個事件組合成一個事件時, throttle 是理想的選擇。

本文為譯文,采用意譯

嗯~

我們來總結下,讀完了上面的內(nèi)容,可以簡單這么理解:

  • 防抖:你可以無限限次觸發(fā),但是在指定的 Delay 時間內(nèi)監(jiān)聽到你沒有新的觸發(fā)事件了,就該我主角上場了。
  • 節(jié)流:不管你觸發(fā)多少次,在指定的 Delay 時間到了以后,我必須上場一次

總結

到此這篇關于JavaScript中防抖和節(jié)流的文章就介紹到這了,更多相關JavaScript防抖和節(jié)流應用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論