最近写了一些前端代码,感觉前端也挺有趣的,因此以后会不定时更新前端的博文。

因为前端这块我也是现学现卖,因此依然重复着需求-学习-实现-发现问题-解决问题-探求原理的过程。

 

问题描述

需求中有一部分是一个滑块控制,用户可以滑动滑块向后台接口传入数值,后台根据传入数值实现某些功能。类似下面这样。

滑块部分实现就不详细说了,就是使用 input type="range" 元素再加上CSS样式美化而已,滑块跟数值的同步则是通过js代码。

在拖动滑块时,触发的oninput事件会通过js调用后台接口,这里的问题是,在滑动滑块时,触发频率太高,例如从10滑到20的过程中,会向后台连续调用10次接口,传入11~20这10个参数。

这显然是有问题的,用户只是想得到20的效果,其他数值是不应该传入的,这些多余的输入会让后台浪费大量性能。

因此这里引入了函数防抖和函数节流的概念,用来控制函数在一定时间内的执行次数,下面分别介绍。

 

函数防抖

函数防抖的概念是创建一个计时器,函数将在计时完毕后再执行(延时执行),如果在计时完毕之前传入新的同一请求,则计时器清零重新等待,最后执行的函数传入的参数是最后一次传入的参数。

函数防抖主要用来防止连续调用,例如输入框内的验证功能、滑块等参数连续修改功能、防止表单多次提交等等。

代码如下

function debounce(fn,wait) {
    var timer = null;
    return function () {
        var context = this;
        var args = arguments;
        if (timer) {
            clearTimeout(timer);
            timer = null;
        }
        timer = setTimeout(function () {
            fn.apply(context,args)
        },wait)
    }
}
var fn = function () {
    console.log('boom')
}
setInterval(debounce(fn,500),1000) //第一次在1500ms后触发,之后每1000ms触发一次
setInterval(debounce(fn,2000),1000) //不会触发,因为一直在重置计时器

其中对timer变量做了一次闭包。

 

函数节流

函数节流的概念是规定一个单位时间,在单位时间内只能触发一次函数执行,其他执行会被忽略。

函数节流主要用于锁定调用频率,例如游戏刷新率、DOM元素拖拽、Canvas画笔功能等。

代码如下

function throttle(fn,gapTime) {
    let _lastTime = null;
    return function () {
        let _nowTime = + new Date();
        if (_nowTime - _lastTime > gapTime || !_lastTime) {
            fn();
            _lastTime = _nowTime;
        }
    }
}
let fn = ()=>{
    console.log('boom')
}
setInterval(throttle(fn,1000),10) //每秒触发一次

其中对_lastTime变量做了一次闭包。

不过这么写有一个问题,即最后一次触发很可能无法执行。

因此可以结合函数防抖实现如下

function throttle2(method,delay,time) {
    var timeout,startTime = new Date();
    return function() {
        var context = this;
        var args = arguments;
        var curTime = new Date();
        clearTimeout(timeout);
        //达到触发间隔则立即触发
        if (curTime - startTime >= time) {
            method.apply(context,args);
            startTime = curTime;
        //最后一次延时触发
        } else {
            timeout = setTimeout(method,delay);
        }
    }
}

或者

var lastTime = null;
var timeout = null;
function throttle(fn,delay) {
    let nowTime = new Date();
    clearTimeout(timeout);
    if (nowTime - lastTime > delay || !lastTime) {
        fn();
        lastTime = nowTime;
    }else{
        timeout = setTimeout(fn,delay);
    }
}

 

总结

在遇到函数触发次数太频繁的问题后,通过查资料找到了这两种方法,这里明显应该使用函数防抖,成功解决了该问题。

当然,在这个案例里也可以使用onchange事件,在松开鼠标的时候才会触发,数值显示则使用oninput实时显示当前数值。

本文代码部分转载自轻松理解JS函数节流和函数防抖

 


所谓自律,是以积极而主动的态度,

去解决人生痛苦的重要原则,

主要包括四个方面:

推迟满足感、承担责任、尊重事实、保持平衡。

《少有人走的路》

——M·斯科特·派克