一、节流的概念和使用库实现节流
1.1. 认识节流
- 节流:当事件第一次被触发时,就会执行相应的回调函数。但是如果这个事件被频繁的触发的话,相应的函数并不会被频繁的执行,而是会以固定的频率执行。不管在这个频率之内触发多少次事件,函数的执行频率都是固定的
1.2. underscore实现节流
- underscore中实现了throttle(节流)方法,调用这个方法,将想要绑定在事件上的回调函数作为参数传入throttle方法即可
_.throttle(fn, time)
<input type="text">
<script src="https://cdn.jsdelivr.net/npm/underscore@1.13.6/underscore-umd-min.js"></script>
<script>
const inputEl = document.querySelector("input")
let count = 0
inputEl.oninput = _.throttle(function() {
console.log("发送网络请求", count++, this.value)
},5000)
</script>
二、手写节流实现
2.1. 基本实现
<body>
<input type="text">
<script>
function zdThrottle(fn, intervalTime) {
let startTime = 0
function _throttle() {
const nowTime = new Date().getTime()
const waitTime = intervalTime - (nowTime - startTime)
if (waitTime <= 0) {
fn()
startTime = nowTime
}
}
return _throttle
}
</script>
<script>
const inputEl = document.querySelector("input")
let count = 0
inputEl.oninput = zdThrottle(function() {
console.log(`发送网络请求${count++}`, this.value)
}, 2000)
</script>
</body>
2.2. this和参数的绑定、取消立即执行
<body>
<input type="text">
<script>
function zdThrottle(fn, intervalTime, leading = true) {
let startTime = 0
function _throttle(...args) {
const nowTime = new Date().getTime()
if (!leading && startTime === 0) {
startTime = nowTime
}
const waitTime = intervalTime - (nowTime - startTime)
if (waitTime <= 0) {
fn.apply(this, args)
startTime = nowTime
}
}
return _throttle
}
</script>
<script>
const inputEl = document.querySelector("input")
let count = 0
inputEl.oninput = zdThrottle(function(event) {
console.log(`发送网络请求${count++}`, this.value, event)
}, 2000, false)
</script>
</body>
2.3. 开启尾部执行:最后就算在规定的时间,事件没有触发,也照常执行一次回调函数
<body>
<input type="text">
<script>
function zdThrottle(fn, intervalTime, { leading = true, trailing = false } = {}) {
let startTime = 0
let timer = null
function _throttle(...args) {
const nowTime = new Date().getTime()
if (!leading && startTime === 0) {
startTime = nowTime
}
const waitTime = intervalTime - (nowTime - startTime)
if (waitTime <= 0) {
if (timer) clearTimeout(timer)
fn.apply(this, args)
startTime = nowTime
timer = null
}
if (trailing && !timer) {
timer = setTimeout(() => {
fn.apply(this, args)
startTime = new Date().getTime()
timer = null
}, waitTime)
}
}
return _throttle
}
</script>
<script>
const inputEl = document.querySelector("input")
let count = 0
inputEl.oninput = zdThrottle(function(event) {
console.log(`发送网络请求${count++}`, this.value, event)
}, 2000, { leading: false, trailing: true })
</script>
</body>
2.4. 取消最后一次执行的功能
<body>
<input type="text">
<button class="cancel">取消</button>
<script>
function zdThrottle(fn, intervalTime, { leading = true, trailing = false } = {}) {
let startTime = 0
let timer = null
function _throttle(...args) {
const nowTime = new Date().getTime()
if (!leading && startTime === 0) {
startTime = nowTime
}
const waitTime = intervalTime - (nowTime - startTime)
if (waitTime <= 0) {
if (timer) clearTimeout(timer)
fn.apply(this, args)
startTime = nowTime
timer = null
}
if (trailing && !timer) {
timer = setTimeout(() => {
fn.apply(this, args)
startTime = new Date().getTime()
timer = null
}, intervalTime)
}
}
_throttle.cancel = function() {
clearTimeout(timer)
}
return _throttle
}
</script>
<script>
const inputEl = document.querySelector("input")
const cancelEl = document.querySelector(".cancel")
let count = 0
const returnValue = zdThrottle(function(event) {
console.log(`发送网络请求${count++}`, this.value, event)
}, 2000, { trailing: true })
inputEl.oninput = returnValue
cancelEl.onclick = function() {
returnValue.cancel()
}
</script>
</body>