JavaScript中防抖和节流的实战应用记录

目录
  • 前言
  • 为什么我们需要防抖/节流
  • 防抖
  • 节流
  • 总结

前言

你可能会遇到这种的情况,一个站点使用自动填充的文本框,内容的拖拽,效果的滚动。那么,你遇到防抖和截流的概率还是很高的。为了使得这些操作,比如自动填充能够顺畅工作,你需要引入防抖和截流功能。

  • 防抖 -> Debounce
  • 节流 -> Throttle

为什么我们需要防抖/节流

开篇已经简单提了,debounce/throttle 能让你的站点表现更优异。它们工作原理是通过减少动作发起的次数。我们简单举个例子,自动填充文本框触发接口请求,如下:

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

上面的事件监测器,监听到输入文本框发生更改,就基于文本框的内容触发一个查询接口。这看起来还不错,但是用户输入 Samantha 文字会发生什么?

当用户输入 S,事件监测器触发请求,并带上选项 S。当此请求正在调用的时候,Sa 输入内容会再次被监听,我们将重新以 Sa 为选项内容发起新的请求。以此类推,这种请求会持续到我们输完 Samantha 的内容。

这会在短时间内发起 8 次请求,但是我们只关心最后一次请求。这意味着前 7 的接口请求都是不必要的,纯属浪费时间和金钱。

为了避免不必要的请求发生,我们就需要防抖和截流。

防抖

我们先来谈下防抖,因为它是解决自动文本框类问题的理想解决方案。防抖的原理是延迟一段时间吊起我们的函数。如果在这个时间段没有发生什么,函数正常进行,但是有内容发生变更后的一段时间触发函数。这就意味着,防抖函数只会在特定的时间之后被触发。

在我们的例子中,我们假设延迟 1 秒触发。也就是当用户停止输入内容后 1 秒,接口强求被吊起。如果我们在 1 秒内输完 Samantha 内容,请求查询内容就是 Samantha。下面我们看看怎么应用防抖。

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

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

上面的防抖函数带有 cb 回调函数参数和一个延时的参数。我们在 debound 函数后返回回调函数,这种包装的方式,保证过了 delay 秒之后,回调函数才会被调用。最后,我们在每次调用 debounce 函数时清楚现有的定时器,以确保我们在延迟完成之前调用 debouce 函数,并重新计时。

这意味着如果用户在 1 秒内,每隔 300毫秒触发一次输入,上面 debouce 函数如下方式工作。

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

不知你注意到没有,即使我们已经输入了 Saman 文案动作超过了一秒中,回调函数也不会调起,知道再过 1 秒钟才被调用。现在,看我们怎么引用防抖函数。

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

我们把请求函数放到回调函数中。

防抖函数在自动填充的情形非常好用,你也可以使用在其他地方,你想将多个触发请求变成一个触发,以缓解服务器的压力。

节流

像防抖一样,节流也是限制请求的多次发送;但是,不同的是,防抖是每隔指定的时间发起请求。举个例子,如果你在 throttle 函数中设置延迟时间是 1 秒,函数被调用执行,用户输入每隔 1秒发起请求。看下下面的应用,你就明白了。

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

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

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

debounce 和 throttle 函数都有一样的参数,但是它们主要的不同是,throttle 中的回调函数在函数执行后立马被调用,并且回调函数不在定时器函数内。回调函数要做的唯一事情就是将 shouldWait 标识设置为 false。当我们第一次调用 throttle 函数,会将 shouldWait 标识设置为 true。这延时的时间内再次调用 throttle 函数,那就什么都不做。当时间超出了延时的时间,shouldWait 标识才会设置为 false。

假设我们每隔 300 毫秒输入一个字符,然后我们的延时是 1 秒。那么 throttle 函数会像下面这样工作:

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

如果你留意看,你会发现第 1200 毫秒的时候,第二次 throttle 函数才会被触发。已经延迟了我们预设时间 200 毫秒。对于节流的需求来说,目前的 throttle 函数已经满足了需求。但是我们做些优化,一旦 throttle 函数中的延时结束,我们就调用函数的前一个迭代。我们像下面这样子应用。

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 函数延时时,后者存储了前一个 args 参数值作为变量 waitingArgs。当延迟完成后,我们会检查 waitingArgs 是否有内容。如果没有内容,我们会将 shouldWait 设置为 false,然后进入下一次触发。如果 waitingArgs 有内容,这就意味着延时到了之后,我们将会带上 waitingArgs 参数触发我们的回调函数,然后重置我们的定时器。

这个版本的 throttle 函数也是延时时间为 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

正如你所看到的,每次我们触发 throttle 函数时,如果延时时间结束,我们要么调用回调函数,要么保存要在延时结束时使用的参数。如果这个参数有值的话,当延时结束时,我们将使用它。这就保证了 throttle 函数在延时结束时获取到最新的参数值。

我们看下怎么应用到我们的例子中。

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

你会发现,我们的应用跟 debounce 函数很相似,除了将 debounce 名改为 throttle。

当然,自动填充文本内容例子,对 throttle 函数并不适用,但是,如果你处理类如更改元素大小,元素拖拉拽,或者其他多次发生的事件,那么 throttle 函数是理想的选择。因为 throttle 每次延时结束时,你都会获得有关事件的更新信息,而 debounce 需要等待输入后延时后才能触发。总的来说,当你想定期将多个事件组合成一个事件时, throttle 是理想的选择。

本文为译文,采用意译

嗯~

我们来总结下,读完了上面的内容,可以简单这么理解:

  • 防抖:你可以无限限次触发,但是在指定的 Delay 时间内监听到你没有新的触发事件了,就该我主角上场了。
  • 节流:不管你触发多少次,在指定的 Delay 时间到了以后,我必须上场一次

总结

到此这篇关于JavaScript中防抖和节流的文章就介绍到这了,更多相关JavaScript防抖和节流应用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 关于JavaScript防抖与节流的区别与实现

    目录 1.防抖 2.节流 3.总结 前言: 作为前端开发中会以下两种需求 (1)搜索需求 搜索的逻辑就是监听用户输入事件,等用户输入完成之后把数据发送给后端,后端返回匹配数据,前端显示数据到页面.如果只要用户输入就发请求,这样会给后端造成请求压力,需要控制请求的频率. (2)页面无限加载数据 页面无限加载数据的逻辑就是监听用户用户滚动事件,在用户滚动的过程中,去请求下一页的数据来显示到页面.,那么只要滚动就去发请求,同样会造成后端请求压力,需要控制请求的频率. 以上两种看起来都是控制请求频率的问

  • JavaScript中防抖和节流的区别及适用场景

    目录 前言 防抖 例如 代码演示 节流 例如 代码演示 前言 防抖和节流,这个在我们的前端生涯中,这两个名词肯定不陌生,甚至经常被人问起: 两者有什么区别? 分别用于什么场景? ps:这就是高频的面试题了吧! 防抖 防抖是什么呢? 形象的的说就是:防止抖动(防抖函数内心独白:“你就抖动吧!等你不抖动了,我们在进行下一步”) 例如 一个搜索输入框, 用户不停的进行输入(这个时候就是抖动的过程), 等用户输入停止之后,再触发搜索. 代码演示 function debounce(fn, delay =

  • 浅谈JavaScript节流与防抖

    目录 节流与防抖 概念: 区别 节流实现 节流函数 防抖实现 防抖函数 防抖升级版 总结 节流与防抖 概念: 区别 节流实现 节流函数 防抖实现 防抖函数 防抖升级版 总结 节流与防抖 背景:当我们频繁去请求资源.接口等其他的时候,就会造成操作Dom频繁,接口压力大等等,性能下降.比如我有时候会每次搜索会猛地敲回车,在网络不很好的时候,点击下一页按钮的时候也会一直点,可能网络不好也可能服务器性能低. 为了避免频繁触发同一事件或请求,这时候就要用到节流和防抖了. what?这是啥?当我第一次听到这

  • JavaScript防抖与节流详解

    目录 防抖debounce 节流throttle 总结 防抖debounce 定义:对于短时间内连续触发的事件,比如滚动事件,防抖就是让某个时间期限内,事件处理函数只执行一次. 关于防抖,拿手指按压弹簧举例,用手指按压弹簧,一直按住,弹簧将不会弹起直到松开手指. 举例resize: function debounce(fn, wait){ var timer = null; return ()=>{ if(timer !== null){ clearTimeout(timer); } timer

  • JavaScript运动框架 解决防抖动问题、悬浮对联(二)

    本文实例是对JavaScript运动框架(一)的应用 scrollTop:有时候网页很长,其高度大于显示器高度,会产生滚动,那么在高度方向上,"滚走"的部分就是scrollTop var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; offsetTop:此属性可以获取元素的上外缘距离最近采用定位父元素内壁的距离,如果父元素中没有采用定位的,则是获取上外边缘距离文档内壁的距离.所谓的定

  • JavaScript深入理解节流与防抖

    目录 一.js防抖和节流 二.为什么滚动scroll.窗口resize等事件需要优化 三.滚动和页面渲染前端性能优化的关系 四.防抖Debounce 1 防抖Debounce情景 2 防抖原理 3 防抖函数简单实现 4 防抖函数的演化过程 ①this event绑定问题 ②立即触发问题 ③返回值问题 ④取消防抖,添加cancel方法 五.节流Throttle 1 节流Throttle情景 2 节流原理 3 节流实现—时间戳和定时器 4 节流函数的演化过程 ①时间戳触发 ②定时器触发 ③结合时间戳

  • JavaScript的防抖和节流一起来了解下

    目录 1.前言 2.函数防抖(debounce) 延迟防抖 前缘防抖 防抖函数实现总结 3.函数节流(throttling) 延迟节流 前缘节流 节流函数实现总结 4.两者区别 5.应用场景 总结 1. 前言 首先来举个例子.百度首页的百度输入框,用户输入的时候,每次输入的信息,我们都能看到百度服务器返回给我们的联想关键字.我们每改动一个字,它就换一次联想词,这是我们肉眼能看到的速度,实际上如果不加以处理,可能已经上服务器发起了好几十次的同一个关键字联想请求了,具体速度依赖于不同的pc等机器上的

  • JavaScript 防抖和节流详解

    目录 防抖 节流 总结 防抖 自动门感应到有人,打开门,并且开始5秒倒计时,在 5 s 内有另外一个人靠近到门,门感应到人,重新5秒倒计时 当事件被触发时,设定一个延迟,若期间事件又被触发,则重新设定延迟,直到延迟结束,执行动作 (防止多次触发) web 应用上面 改变页面大小的统计 滚动页面位置的统计 输入框连续输入的请求次数控制 一开始,点击按钮,console.log('pay money') <body> <button id="btn">click&l

  • javascript的防抖和节流你了解吗

    一:为什么需要防抖与节流 防抖和节流都是为了解决短时间内大量触发某函数或者事件而导致的性能问题,比如在 1.用户体验上,触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死,卡顿的现象 2.服务器上:加重服务器压力 二:防抖 防抖是当事件或函数被触发后,延迟n秒后在执行回调,如果在这n秒内事件或函数又被触发,则重新计时,直到n秒内没有触发事件或函数,则执行回调函数 图文解释: (回城的时间就相当于延迟时间,如果在回城的时间内再次触发回城则重新倒计时回城时间) 案例:表单输入框事件 <!DOC

  • JavaScript防抖与节流的实现与注意事项

    目录 概念 实现 测试 注意事项 总结 概念 防抖:点击N次提交按钮,只有最后一次会发出请求.减少无效请求的次数. 节流:每点击一次按钮,都会失效一段时间.降低触发的频率. 实现 /* 防抖 时限内,只有最后一次调用会执行 */ function debounce(func, interval = 0) { let timer; return function () { if (timer) { clearTimeout(timer); } timer = setTimeout(() => {

  • JavaScript防抖动与节流处理

    针对一些会频繁触发的事件如scroll.resize,如果正常绑定事件处理函数的话,有可能在很短的时间内多次连续触发事件,十分影响性能. 因此针对这类事件要进行防抖动或者节流处理 防抖动 它的做法是限制下次函数调用之前必须等待的时间间隔.正确实现 debouncing 的方法是将若干个函数调用合成 一次,并在给定时间过去之后仅被调用一次. // 将会包装事件的 debounce 函数 function debounce(fn, delay) { // 维护一个 timer let timer =

  • 通过实例讲解JS如何防抖动

    前言 这道题目经常与事件触发器同时存在,为了考察面试者在一些具体业务流程上(信息流,搜索框输入查询)等,能否综合的考虑实现思路. 题目 在某些信息列表中一般采用瀑布流,滚动一屏时加载相应的数据,请思考如何避免连续下拉时而产生的问题(可能是页面崩溃,也可能是巨卡无比). 一般情况下,如果碰到这样的面试题,防抖动机制,就能很好的解决,这方面最早的应用实践还是Twitter,开发者写了一篇博客,详细的阐述了如何解决这样的问题.那么,说到防抖动,其核心内涵在于延迟处理,也就是将一系列的事件处理程序全部延

  • JavaScript中函数的防抖与节流详解

    目录 一.函数的节流 1.1定义 1.2解决方法 1.3案例演示 1.3.1 代码演示 1.3.2 运行结果 1.3.3 添加函数节流操作 1.3.4 运行结果 二.函数的防抖 2.1 定义 2.2 解决方法 2.3 案例演示 2.3.1 代码展示 2.3.2 运行结果 2.3.3添加函数防抖操作 2.3.4 运行结果 总结 一.函数的节流 1.1 定义 同时触发多次函数执行,执行的是相同内容,要求只执行第一次请求. 例如scroll事件,鼠标滚动一次触发多次函数执行,只需要执行一次. 1.2

  • JavaScript函数防抖动debounce

    目录 防抖动简述 防抖应用示例 1.防抖前后示例效果截图 2.防抖示例完整 demo 防抖动的应用场景 防抖动简述 函数防抖动(debounce):防止在短时间内过于频繁的执行相同的任务. 当短时间内的频繁是不必要的时候,就可以考虑去抖动,避免资源浪费,或造成不好体验. 函数防抖动的原理,主要是利用一次性定时器,延迟任务的执行,在延迟这段时间内, 如果任务再次被触发,则通过 clearTimeout 销毁上一次产生的定时器, 因为定时器的被销毁,之前被延迟执行的任务也会随之被取消执行. 这样就实

随机推荐