js节流函数

2019-05-23153次阅读javascript

js有些事件例如resize,mousemove等是会不间断触发的,如果这些事件的回调函数里有操作DOM、复杂算法或者Ajax向服务器发送请求等就会严重影响性能及其资源浪费。为了避免这个问题,我们一般会使用定时器来对函数进行节流。

函数节流的基本思想是设置一个定时器,在指定时间间隔内运行代码时清除上一次的定时器,并设置另一个定时器,直到函数请求停止并超过时间间隔才会执行。

在《JavaScript高级程序设计》中有专门应对此问题的函数节流

function throttle(method,context){
    clearTimeout(method.tId);
    method.tId=setTimeout(function(){
        method.call(context)
    },300)
}
window.onresize=throttle(resizehandler);

throttle函数接收两个参数,即要执行的函数及执行环境,如果执行环境未定义,默认则为windows。在这个函数中,会在第一次执行时为method定义一个定时器属性,在指定时间间隔(300)内再次执行时会清除上一次定义的定时器并创建新定时器让函数在300毫秒后执行。

再看看下面这个函数节流方案

function throttle(method,delay){
    var timer=null;
    return function(){
        var context=this, args=arguments;
        clearTimeout(timer);
        timer=setTimeout(function(){
            method.apply(context,args);
        },delay);
    }
}
window.onresize=throttle(resizehandler,500);

调用结果和第一种结果相同,都能有效的阻止函数重复调用,不同的是,第一种将定时器设置为函数的一个属性,而第二种方案通过闭包来实现。

以上两种方案存在一个问题,即如果事件一直触发,那么函数将永远不会被执行,这在某些时候并不符合我们的需求(例如像百度首页输入自动提示一样的东西,在text上绑定keyup事件,每次键盘弹起的时候自动提示,但是又不想提示那么频繁,于是用了上面方法,但是悲剧了,只有停止输入等500毫秒才会提示,在输入过程中根本就没有提示。只要是用户会盲打,在500毫秒内按一下键盘,提示函数就会不断被延迟,这样只有停下来的时候才会提示,这就没意义了),我们只是想在规定时间内减少函数执行次数,所以对以上函数做如下改进:

function throttle(method,delay,duration){
    var timer=null, begin=+new Date();
    return function(){
        var context=this, args=arguments, current=+new Date();
        clearTimeout(timer);
        if(current-begin>=duration){
            method.apply(context,args);
            begin=current;
        }else{
            timer=setTimeout(function(){
                method.apply(context,args);
            },delay);
        }
    }
}
window.onresize=throttle(resizehandler,250,500);

当然如果只考虑IE9+,下面这个也可以试试:

function throttle(action, wait = 1000) {
    let time = Date.now();
    return function() {
        if ((time + wait - Date.now()) < 0) {
            action();
            time = Date.now();
        }
    }
}

为了使(节流后的)滚动更平滑,你也可以通过使用IE10+的window.requestAnimationFrame()来实现函数节流:

function throttle(action) {
    let isRunning = false;
    return function() {
        if (isRunning) return;
        isRunning = true;
        window.requestAnimationFrame(() => {
            action();
            isRunning = false;
        });
    }
}

当然,你可以通过现有的开源轮子来实现,就像Lodash或 _.throttle 。

上一篇: ES6之Proxy  下一篇: gulp-clean-css压缩支持IE8选项配置  

js节流函数相关文章