什么是节流函数?
当事件触发时,会触发事件的响应函数,如果这个事件频繁的触发,那么这个节流函数会按照一定的频率执行函数,不管中间有多少次触发这个事件,执行函数的频率是固定的。简单来说:我在10s内触发了十次事件,但是我设置的节流函数是10s的周期触发一次,那么只会执行一次事件的业务代码。
所以我们需要一个时间频率interval为2s,表示在几秒内触发一次事件,
nowTime是下一次触发事件时间,lastTime是上一次触发事件的时间,所以当两者的差大于等于interval的时候,也就是触发下一次响应函数的时候,
interval - (nowTime - lastTime)
基本实现
throttle.js
function throttle(fn, interval) {
let lastTime = 0
const _throttle = function() {
const nowTime = new Date().getTime()
const remainTime = interval - (nowTime - lastTime)
//当触发响应函数时,将nowTime赋值给lastTime,也就是保存当前函数触发的时间,用于和下一次做时间间隔作比较。
if (remainTime <= 0) {
fn()
lastTime = nowTime
}
}
return _throttle
}
throttle.html
<input type="text">
<button>取消</button>
<script src="./throttle.js">
</script>
<script>
let inp = document.querySelector('input')
let btn = document.querySelector('button')
let count = 0
function fn() {
count++
console.log(`发送了` + count + '次请求');
}
inp.oninput = throttle(fn, 2000)
</script>
以上的代码会在刚开始的运行的时候,会自动触发一次响应函数,那么为什么会触发呢? 原因是因为nowTime刚开始是一个非常大的时间数字,lastTime刚开始默认是0,所以这里的remainTime是小于零的,所以第一次响应函数是默认执行的。
解决第一次事件不要立即触发
所以我们还需要一个变量来控制第一次是否执行,申明一个对象,里面保存变量leading默认为false,我们上面已经分析过第一次就执行的问题,所以这里我们只需要将lastTime在leading
function throttle(fn, interval,options = {leading:false}) {
let lastTime = 0
//将其解构出来
const {leading} = options
const _throttle = function() {
const nowTime = new Date().getTime()
const remainTime = interval - (nowTime - lastTime)
//当触发响应函数时,将nowTime赋值给lastTime,也就是保存当前函数触发的时间,用于和下一次做时间间隔作比较。
if (remainTime <= 0) {
fn()
lastTime = nowTime
}
}
return _throttle
}
解决最后一次执行
也就是我们不停的发送请求,当最后一次发送请求在节流周期之内,比如我们在第10s-第12s开始,我们发送请求发送到一半,突然不发送了假设我们到达了11s,本来这个时候应该是不执行请求的,因为此时并没有到达12s发送请求的临界值,这个时候我们就让他把这个请求自动发送出去。 可能比较难理解。 于是我们将最后剩余的时间加入定时器,利用定时器来执行最后一次任务,所以加入以下代码
if(trailing){
setTimeout(()=>{
fn()
},remainTime)
}
但是这样一来加入的定时器就太多了,当我们在输入框输入内容的时候,也就是模拟发送的请求,我们每输入一个内容,就会产生一个remainTime,那么这么一来,就会产生很多定时器,然后就会出现这样的情况;我输入了七次内容,发送了八次请求;所以定时器这里还得进行限制。让他在我们停止输入内容的时候进行定时器发送。
改进1
let t= null
if(remainTime <= 0){
if(t){
clearTimeout(t)
t = null
}
}
if(trailing){
t = setTimeout(()=>{
t = null
fn()
},remainTime)
}
function throttle(fn, interval, options = { leading: false, trailing: false }) {
// 表示第一次是触发的,最后一次不触发
let lastTime = 0
const { leading, trailing } = options
let timer = null
const _throttle = function() {
let nowTime = new Date().getTime()
if (!leading && !lastTime) {
lastTime = nowTime
}
let remainTime = interval - (nowTime - lastTime)
if (remainTime <= 0) {
if (timer) {
clearTimeout(timer)
timer = null
}
fn()
lastTime = nowTime
return
}
//
if (trailing && !timer) {
timer = setTimeout(() => {
// console.log();
fn()
lastTime = !leading ? 0 : new Date().getTime()
}, remainTime)
}
}
return _throttle
}