节流(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应用加上"冷却时间",让它运行得更流畅!🚀

相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60613 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax