浅谈函数防抖和节流
函数防抖和接口
debounce 函数防抖
函数防抖,从字面上理解就是希望函数被频繁调用的时候,在指定时间间隔内,只会被调用一次。
throttle 函数节流
函数节流,从字面上理解就是希望函数在指定间隔内(按照我们设定的频次)触发一次。
其实说白了,函数防抖和函数的节流的最终目的还是为了节省资源。比如防止过于频繁的请求接口服务等等。根据两者特性,我也模拟出一种场景来对两者进行解释。具体场景如下:某当红明星在某个城市开演唱会,某麦上提供抢票,如何避免用户频繁点击抢票按钮向服务端发送请求,可以运用防抖或者节流来进行处理:
上述图片中确认和下单按钮都能触发抢票的接口服务,为了实现能够有效的控制用户的请求,可以使用防抖或者是节流的方法进行处理,其中下单使用了函数防抖,而确认则用了函数节流,代码使用vue场景进行实现:
debounce
function debounce (func, delay) {
delay = delay || 500
let timer = null
retun function () {
let _this = this
let _args = arguments
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(_this, _args)
}, delay)
}
}
上述代码执行在触发事件后函数 delay毫 秒后才执行,如果我在触发事件后的delay毫秒内又重新触发了事件,则会重新计算函数执行时间。(如果设置delay为1000,那么使用func debounce 后,1秒会执行所要的操作,如果用户在1秒内又去触发要执行的操作,那么debounce会重新被触发,并且延时1秒后执行,也就是说你不停的去触发要执行的函数时,间隔时间小于1s内都不会被触发成功),如果想要立即执行,需要对debounce进行改造:
function debounce (func, delay) {
delay = delay || 500
let timer = null
retun function () {
let _this = this
let _args = arguments
let handleNow = !time
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
timer = null
}, delay)
if (handleNow) func.apply(_this, _args)
}
}
执行函数立即执行的代码于最初的代码的区别在于一个是delay后看到效果,一个是立即看到效果。
throttle
function throttle (func, delay) {
let previous = 0
let timer = null
return function () {
let _this = this
let _args = arguments
let timeNow = new Date().getTime()
if ((timeNow - previous) > delay) {
func.apply(_this, _args)
previous = timeNow
}
}
}
上述代码执行在持续触发事件过程中,执行函数会立即执行,并且每 delay毫秒执行一次,如果要让他在指定时间间隔内触发(类似定时器版),需要对代码做改造:
function throttle (func, delay) {
let previous = 0
let timer = null
return function () {
let _this = this
let _args = arguments
if (!timer) {
timer = setTimeout(() => {
timer = null
func.apply(_this, _args)
}, delay)
}
}
}
设置了具有时间间隔的函数节流,实现过程主要是在持续触发事件的过程中,需要执行的函数不会立即被执行,并且每间隔delay毫秒执行一次,在停止触发事件后,执行函数还是会被再执行一次。
其实无论是函数防抖的间隔版本和立即执行版本还是函数节流的间隔版本还是立即执行版本,看到代码后还是很好理解的。这样做也确实是能够非常有效的节省计算机资源,减少资源消耗。其实对于debounce的两个版本还有throttle的两个版本可以合并起来的:
// common.js
// 防抖函数
export const debounce = (func, delay, immediate) => {
delay = delay || 500
let timer = null
return function () {
let _this = this
let _args = arguments
if (timer) clearTimeout(timer)
if (immediate) {
let isCallNow = !timer
timer = setTimeout(() => {
timer = null
},delay)
if (isCallNow) func.apply(_this, _args)
} else {
timer = setTimeout(() => {
func.apply(_this, _args)
}, delay)
}
}
}
// 函数节流
export const throttle = (func, delay, type) => {
let previous = 0
let timer = null
return function() {
let _this = this
let _args = arguments
if(type === 1){
let now = Date.now()
if (now - previous > delay) {
func.apply(_this, _args)
previous = now
}
}else if(type === 2){
if (!timer) {
timer = setTimeout(() => {
timer = null
func.apply(_this, _args)
}, delay)
}
}
}
}
vue 页面 + js 代码实现
<template>
<div class="test-container">
<div class="top">
</div>
<div class="m-t-24 t-c">
抢票请求1:{{times}}
</div>
<div class="m-t-24 t-c">
抢票请求2:{{dataTimes}}
</div>
<div class="btn">
<span @click="cancelOrder()">确认</span>
<span @click="confirmOrder()">下单</span>
</div>
</div>
</template>
<script>
import { debounce, throttle } from '../util/common'
import 'vconsole'
import VButton from "../components/form/VButton";
import {dateFormat} from "../util/DateFormat";
export default {
name: 'test',
components: {
VButton
},
data() {
return {
times: 0,
dataTimes: 0
}
},
computed: {},
created() {
},
updated() {
console.log('updated')
},
watch: {},
methods: {
confirmOrder:debounce(function () {
this.changCounts()
}, 1000, false),
changCounts () {
this.times += 1
},
showInfo () {
this.dataTimes += 1
},
cancelOrder:throttle(function () {
this.showInfo()
}, 2000, 2)
}
}
</script>
<style lang="scss" scoped>
.test-container {
width: 100%;
height: 100%;
.top {
height: 250px;
border-bottom: 1px solid #e9ecf0;
background-image: url("../assets/img/posters.png");
background-size: cover;
}
.btn {
cursor: pointer;
height: 45px;
line-height: 45px;
width: 100%;
position: fixed;
bottom: 0;
border-top:1px solid #e9ecf0;
background-color: #FF69B4;
span {
text-align: center;
display: inline-block;
color: white;
width: 49%;
}
}
}
</style>