前言
大家好,作者之前在防抖节流(一)中介绍了一下防抖节流中的防抖,详细阐述了它的应用以及实现代码,这篇文章将要介绍一下它的好兄弟--节流。
什么是节流
那么什么是节流呢?
节流(throttle)是通过限制函数执行的频率,在一定时间内只执行一次,以减少资源消耗或优化性能。
它在页面中的效果是这样的:

注意看控制台:在用户输入时,无论用户输入的频率如何,节流帮我们控制了让输入框内容的输出只有在一秒内才能输出一次。
大家都打过fps游戏吧,不论你的鼠标左键点击多少次,最终枪械的输出次数只取决于它的射速,这就类似于节流的效果。
为什么要节流?
那么为什么要节流呢?
有一些典型的应用场景:
-
滚动事件(scroll)
- 问题:滚动页面时会高频触发
scroll
事件,如果回调函数涉及 DOM 计算、动画等操作,会导致卡顿。 - 节流作用:限制为每 100ms 执行一次,减少不必要的计算。
- 问题:滚动页面时会高频触发
-
窗口调整(resize)
- 问题:拖拽调整窗口大小时,
resize
事件会连续触发,若回调函数需要重新布局页面,可能引发性能瓶颈。 - 节流作用:确保只在调整结束后或固定间隔执行一次布局计算。
- 问题:拖拽调整窗口大小时,
-
按钮防连点(点击提交)
- 问题:用户快速点击提交按钮时,可能发送多次重复请求。
- 节流作用:限制点击间隔(如 1 秒内只能提交一次),避免重复请求。
节流的实现
接下来看看节流的实现吧
节流的实现代码如下:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</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;
return function(args){
let that = this;
let now = + new Date();
if (last && now < last + delay){
clearTimeout(deferTimer);
deferTimer = setTimeout(function() {
last = now;
fn.call(that,args);
},delay);
} else{
last = now;
fn.call(that,args);
}
}
}
let throttleAjax = throttle(ajax,1000);
inputC.addEventListener('keyup',function(e) {
throttleAjax(e.target.value);
})
</script>
</body>
</html>
了解过上一篇介绍防抖的实现代码的朋友们看到这里会发现,防抖的代码结构和节流很类似,是的,它们都是用高阶函数实现+闭包特性实现的,下面是详细解读
- (一)定义
ajax
函数
js
const ajax = function(content) { //被节流的函数
console.log("ajax request" + content)
}
ajax
是一个模拟的请求函数,在实际应用中可能是 fetch
或 XMLHttpRequest
请求。
- (二)实现
throttle
节流函数
js
function throttle(fn, delay) {
let last, deferTimer; // 记录上次执行时间、定时器
return function(args) {
let that = this; // 保存 this 指向
let now = +new Date(); // 当前时间戳
if (last && now < last + delay) { // 如果在节流间隔内
clearTimeout(deferTimer); // 清除之前的定时器
deferTimer = setTimeout(function() {
last = now; // 更新时间戳
fn.call(that, args); // 执行函数
}, delay);
} else { // 如果不在节流间隔内
last = now; // 更新时间戳
fn.call(that, args); // 立即执行函数
}
}
}
这里是节流实现的核心部分:
-
last
:记录上次执行fn
的时间戳。 -
deferTimer
:存储setTimeout
的 ID,用于清除未执行的延迟调用。 -
now
:获取当前时间戳(+new Date()
转化为number类型,方便等下计算)。 -
节流判断:
if (last && now < last + delay)
:
如果 last
存在(非首次调用),并且当前时间距离上次调用时间小于 delay
(仍在节流间隔内),则:
go
清除之前的 `setTimeout`(避免堆积多个延迟调用)。
重新设置一个 `setTimeout`,在 `delay` 时间后执行 `fn`。
else
:
go
如果不在节流间隔内(首次调用或间隔已过),则:
立即执行 `fn`,并更新 `last` 时间戳。
- (三)应用节流函数
js
let throttleAjax = throttle(ajax, 1000); // 1秒内只执行一次
inputC.addEventListener('keyup', function(e) {
throttleAjax(e.target.value); // 传入输入框的值
});
throttle(ajax, 1000)
:生成一个节流版本的 ajax
函数,确保 1 秒内最多执行一次。
keyup
事件监听:
go
每次输入时,调用 `throttleAjax`,传入输入框的值。
如果用户在 1 秒内连续输入,`ajax` 不会立即触发,而是:
第一次输入时立即执行 `ajax`。
后续输入会进入 `setTimeout`,并在 1 秒后执行(如果期间没有新的输入)。
执行流程示例
假设用户快速输入 abc
:
-
输入
a
:last
未定义 → 立即执行ajax("a")
,并记录last = now
。
-
输入
b
(0.5 秒后) :now < last + 1000
(仍在 1 秒内)→ 清除之前的setTimeout
,重新设置 1 秒后执行ajax("b")
。
-
输入
c
(0.8 秒后) :now < last + 1000
(仍在 1 秒内)→ 再次清除setTimeout
,重新设置 1 秒后执行ajax("c")
。
-
最终结果:
ajax("a")
立即执行。ajax("c")
在 1 秒后执行(b
被覆盖)。
总结
上面就是关于节流知识的全部内容了,希望该文章对你有帮助!