节流(throttle):给频繁操作加上冷却时间!🔥

节流(throttle):给频繁操作加上冷却时间!🔥

在游戏里,你放了一个大招,是不是要等冷却时间结束后才能再次释放?在 JavaScript 的世界里,也有一种技术叫做"节流",它就像给技能的释放加上了冷却时间,避免频繁操作导致页面卡顿甚至崩溃。今天,我们就来聊聊节流的底层机制,它和防抖(debounce)的区别,以及为什么我们需要它。

当事件变得"太热情" ❤️‍🔥

想象一下,你在一个搜索框中输入关键词,页面会根据输入内容实时联想并展示结果。看起来很方便,对吧?但是,如果用户输入速度很快,那么每按一次键都会触发一次网络请求,这可能会导致:

  1. 大量请求在短时间内发出,服务器压力剧增
  2. 请求返回顺序不一致,导致页面展示混乱
  3. 浏览器性能下降,页面卡顿甚至崩溃

再比如,你在滚动页面时,需要计算某些元素的位置(比如懒加载图片),如果每次滚动都触发,那么滚动一下可能会触发几十次事件!

这时候,我们就需要两种技术来优化:防抖(debounce)节流(throttle) 。在防抖(Debounce)的底层机制:闭包与this指向的深度解析 🚀防抖(Debounce)的底层机制:闭包与this - 掘金这篇文章中已经介绍过防抖了,今天,我们重点讲解节流。

节流的基本概念 ⏱️

节流(throttle)的核心思想是:固定时间间隔内只执行一次操作。也就是说,无论事件触发多么频繁,执行函数都会按照固定的时间间隔执行一次。

节流的底层机制 🧠

让我们通过一个具体的例子来理解节流的实现。以下是一个常见的节流函数实现:

javascript 复制代码
function throttle(fn, delay) {
  let last; // 上一次执行的时间戳
  let deferTimer; // setTimeout的ID

  return function(...args) {
    let that = this;
    let now = +new Date(); // 当前时间戳

    // 如果上次执行时间存在,且当前时间小于上次执行时间+延迟时间(即还在冷却中)
    if (last && now < last + delay) {
      clearTimeout(deferTimer); // 清除之前设置的定时器
      deferTimer = setTimeout(function() {
        last = now; // 更新上一次执行时间为当前时间
        fn.apply(that, args); // 执行函数
      }, delay);
    } else {
      // 第一次执行或者冷却时间已过
      last = now;
      fn.apply(that, args);
    }
  };
}

代码解析:

  1. 闭包保存状态lastdeferTimer 被闭包保存,用于记录上一次执行的时间和定时器ID。

  2. 返回函数 :当我们调用 throttle(ajax, 2000) 时,它返回一个新的函数(即 throttleAjax),这个函数被绑定到事件监听器上。

  3. 时间戳判断 :每次事件触发时,获取当前时间戳 now,并与上一次执行的时间戳 last 进行比较:

    • 如果距离上次执行的时间小于设定的间隔 delay,说明还在冷却期
    • 否则,说明已经过了冷却期,可以立即执行
  4. 定时器的作用 :在冷却期内,每次事件触发都会重置定时器,保证在最后一次事件触发后的 delay 时间后执行一次函数。

这样,我们就实现了:在单位时间内,最多执行一次函数。

节流 vs 防抖:区别在哪? 🥊

这张图片形象地表示了地表示了debouncethrottle时间触发的频率的区别

很多人容易混淆节流和防抖,它们虽然都是用来控制函数执行频率的,但行为不同:

特性 节流(throttle) 防抖(debounce)
执行时机 固定时间间隔执行一次 事件停止触发后延迟执行
执行次数 单位时间内至少执行一次 只执行最后一次触发的事件
适用场景 滚动事件、窗口调整、输入联想 搜索框输入验证、表单提交
类比 技能冷却时间,固定间隔释放 电梯关门,等待最后一个人进入

用一个生活场景比喻:

  • 防抖:电梯门要等最后一个人进入后,等待一段时间(比如5秒)再关门
  • 节流:电梯门每隔10秒自动尝试关闭一次,如果有人进入就重新等待,但每隔10秒总会尝试关闭

为什么我们需要节流? 💡

节流的主要目的是优化性能避免不必要的资源浪费。具体来说:

  1. 减少函数执行频率:对于频繁触发的事件(如滚动、鼠标移动、窗口调整大小),节流可以大幅减少事件处理函数的执行次数,从而减轻浏览器负担。

  2. 保证用户体验:在搜索联想场景中,我们可能不需要每次按键都发送请求,而是每隔一定时间发送一次,这样既能减少请求数量,又能保证用户看到实时反馈。

  3. 避免请求混乱:在输入联想时,如果每次按键都发送请求,那么请求返回的顺序可能不一致,导致最终展示的结果不是最新的。通过节流,我们可以确保请求按固定间隔发送,减少混乱。

实际应用场景 🚀

1. 滚动事件优化

javascript 复制代码
// 监听滚动事件,但最多每100ms执行一次
window.addEventListener('scroll', throttle(function() {
  // 计算元素位置、懒加载图片等
}, 100));

2. 窗口调整大小

javascript 复制代码
// 窗口调整大小时重新布局,但避免过于频繁
window.addEventListener('resize', throttle(function() {
  // 重新计算布局
}, 200));

3. 输入联想搜索

javascript 复制代码
// 搜索框输入联想,每500ms发送一次请求
input.addEventListener('input', throttle(function(e) {
  // 发送搜索请求
  fetchResults(e.target.value);
}, 500));

4. 游戏中的技能释放

javascript 复制代码
// 防止玩家连续点击技能按钮
attackButton.addEventListener('click', throttle(function() {
  // 释放技能
  castSkill();
}, 1000)); // 技能冷却时间1秒

完整代码示例 🧩

下面是文章开头提供的完整代码,实现了输入框的节流功能:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>节流示例</title>
</head>
<body>
    <input type="text" id="inputC"/>
    <script>
        let inputC = document.getElementById('inputC')
        const ajax = function(content){//被节流的函数 
            // 模拟网络请求
            console.log('ajax request: ' + content)
        }
        // 节流函数
        function throttle(fn,delay){
            let last,//上一次执行的时间
            deferTimer;//timeout id
            return function(...args){
                let that = this;
                let now = +new Date();//当前时间戳
                if(last && now <last + delay){
                    clearTimeout(deferTimer);//清除定时器
                    deferTimer = setTimeout(function(){
                        last = now;
                        fn.apply(that,args)
                    },delay)
                }else{
                    last = now;
                    fn.apply(that,args)
                }
            }
        }
        // 创建节流版本的ajax函数
        let throttleAjax = throttle(ajax, 500)
        // 监听输入框的keyup事件
        inputC.addEventListener('keyup',function(e){
            throttleAjax(e.target.value)
        })
    </script>
</body>
</html>

在这个例子中,无论用户输入多快,ajax 函数至少会每500ms执行一次(防抖:只要用户输入的足够快,函数可能很久都不会执行)。这样,我们就有效地控制了请求频率。

总结 🎯

节流是一种非常实用的性能优化技术,它通过固定时间间隔内只执行一次函数,有效控制了函数执行的频率。与防抖不同,节流保证了在单位时间内至少执行一次,适用于需要持续反馈的场景(如滚动事件)。

在实际开发中,我们应该根据具体需求选择使用节流还是防抖:

  • 如果你关心最终状态,比如输入结束后的验证,使用防抖
  • 如果你需要固定间隔执行,比如滚动事件,使用节流

最后,希望你在项目中合理使用节流技术,为你的Web应用加上"冷却时间",让它运行得更流畅!🚀

相关推荐
Rainbow_Pearl1 分钟前
Vue2_element 表头查询功能
javascript·vue.js·elementui
Java技术小馆29 分钟前
langChain开发你的第一个 Agent
java·面试·架构
aha-凯心37 分钟前
前端学习 vben 之 axios interceptors
前端·学习
熊出没1 小时前
Vue前端导出页面为PDF文件
前端·vue.js·pdf
VOLUN1 小时前
Vue3项目中优雅封装API基础接口:getBaseApi设计解析
前端·vue.js·api
此乃大忽悠1 小时前
XSS(ctfshow)
javascript·web安全·xss·ctfshow
用户99045017780091 小时前
告别广告干扰,体验极简 JSON 格式化——这款工具让你专注代码本身
前端
前端极客探险家1 小时前
告别卡顿与慢响应!现代 Web 应用性能优化:从前端渲染到后端算法的全面提速指南
前端·算法·性能优化
袁煦丞2 小时前
【局域网秒传神器】LocalSend:cpolar内网穿透实验室第418个成功挑战
前端·程序员·远程工作
江城开朗的豌豆2 小时前
Vuex数据突然消失?六招教你轻松找回来!
前端·javascript·vue.js