欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

前言

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

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

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

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

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

??上面的事件監(jiān)測(cè)器,監(jiān)聽(tīng)到輸入文本框發(fā)生更改,就基于文本框的內(nèi)容觸發(fā)一個(gè)查詢(xún)接口。這看起來(lái)還不錯(cuò),但是用戶(hù)輸入 Samantha 文字會(huì)發(fā)生什么?

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

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

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

防抖

我們先來(lái)談下防抖,因?yàn)樗墙鉀Q自動(dòng)文本框類(lèi)問(wèn)題的理想解決方案。防抖的原理是延遲一段時(shí)間吊起我們的函數(shù)。如果在這個(gè)時(shí)間段沒(méi)有發(fā)生什么,函數(shù)正常進(jìn)行,但是有內(nèi)容發(fā)生變更后的一段時(shí)間觸發(fā)函數(shù)。這就意味著,防抖函數(shù)只會(huì)在特定的時(shí)間之后被觸發(fā)。

在我們的例子中,我們假設(shè)延遲 1 秒觸發(fā)。也就是當(dāng)用戶(hù)停止輸入內(nèi)容后 1 秒,接口強(qiáng)求被吊起。如果我們?cè)?1 秒內(nèi)輸完 Samantha 內(nèi)容,請(qǐng)求查詢(xún)內(nèi)容就是 Samantha。下面我們看看怎么應(yīng)用防抖。

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

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

上面的防抖函數(shù)帶有 cb 回調(diào)函數(shù)參數(shù)和一個(gè)延時(shí)的參數(shù)。我們?cè)?debound 函數(shù)后返回回調(diào)函數(shù),這種包裝的方式,保證過(guò)了 delay 秒之后,回調(diào)函數(shù)才會(huì)被調(diào)用。最后,我們?cè)诿看握{(diào)用 debounce 函數(shù)時(shí)清楚現(xiàn)有的定時(shí)器,以確保我們?cè)谘舆t完成之前調(diào)用 debouce 函數(shù),并重新計(jì)時(shí)。

這意味著如果用戶(hù)在 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 文案動(dòng)作超過(guò)了一秒中,回調(diào)函數(shù)也不會(huì)調(diào)起,知道再過(guò) 1 秒鐘才被調(diào)用?,F(xiàn)在,看我們?cè)趺匆梅蓝逗瘮?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)
)}

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

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

節(jié)流

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

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í)器函數(shù)內(nèi)?;卣{(diào)函數(shù)要做的唯一事情就是將 shouldWait 標(biāo)識(shí)設(shè)置為 false。當(dāng)我們第一次調(diào)用 throttle 函數(shù),會(huì)將 shouldWait 標(biāo)識(shí)設(shè)置為 true。這延時(shí)的時(shí)間內(nèi)再次調(diào)用 throttle 函數(shù),那就什么都不做。當(dāng)時(shí)間超出了延時(shí)的時(shí)間,shouldWait 標(biāo)識(shí)才會(huì)設(shè)置為 false。

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

// 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

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

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

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

這個(gè)版本的 throttle 函數(shù)也是延時(shí)時(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ù)時(shí),如果延時(shí)時(shí)間結(jié)束,我們要么調(diào)用回調(diào)函數(shù),要么保存要在延時(shí)結(jié)束時(shí)使用的參數(shù)。如果這個(gè)參數(shù)有值的話(huà),當(dāng)延時(shí)結(jié)束時(shí),我們將使用它。這就保證了 throttle 函數(shù)在延時(shí)結(jié)束時(shí)獲取到最新的參數(shù)值。

我們看下怎么應(yīng)用到我們的例子中。

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

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

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

本文為譯文,采用意譯

嗯~

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

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

總結(jié)

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

相關(guān)文章

最新評(píng)論