资讯专栏INFORMATION COLUMN

javaScript的Throttling(节流)和Debouncing(防抖)

Yujiaao / 1945人阅读

摘要:节流和防抖都是用来提高用户体验,提高网站性能的手段,它们的技术手段都是强制事件处理函数在特定的事件段内执行。针对上面举例的情况,其实运用节流和防抖都可以做到,只是它们之间又有一定的区别节流节流是在一定的时间段内,函数最多可以被调用多少次。

“节流”和“防抖”都是用来提高用户体验,提高网站性能的手段,它们的技术手段都是“强制事件处理函数在特定的事件段内执行”。这样解释可能还是不够直观。举两个例子吧:

1: 比方说我们给document绑定了一个scroll的事件,scroll事件是每滑动一个px,scroll的处理函数就会被调用执行,如果在你的处理函数里面恰巧做了一个很花时间或者很花空间的事情,比方说复杂的运算啊,ajax请求啊,那这样页面就可能出现卡顿的情况。

2: 页面上有个地址的输入框,你希望根据客户的输入内容,去帮客户补全。假如说这个地址列表需要通过ajax请求来获取,那我们一定是希望在客户停止输入了之后再去请求ajax然后来补全,而不是客户一边输入就一直请求ajax。

针对上面举例的情况,其实运用节流和防抖都可以做到,只是它们之间又有一定的区别:

节流:节流是在一定的时间段内,函数最多可以被调用多少次。也可以理解为函数以一定的频率被调用。
防抖:防抖是每次想要执行这个函数,都得事先等上一段时间。

语言总是这样苍白,直接来看代码的实现吧。我们先来实现一个防抖:

//实现防抖函数
function debouncing(fn, waitTime){
    let timer = undefined;
    return function(){
        let context = this;
        let args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function(){
            fn.apply(context, args);
        }, waitTime)
    }
}
//scroll事件的处理函数
function scrollHandler(event){
    console.log(new Date(event.timeStamp));
}
//document的scroll事件上使用防抖函数
document.addEventListener("scroll", debouncing(scrollHandler,100), false);

实现防抖函数的核心就是每次事件被触发的时候,我们不是立即去调用相应的handler,而是每一次都重新设置一个timeout,等待一段时间,然后再执行我们的handler.

2: 现在来尝试实现一个节流函数:

function throttling(fn, intervalTime){
    let inInterval = false;
    return function(){
        let context = this;
        let args = arguments;
        if(!inInterval) {
            fn.apply(context, args);
            inInterval = true;
            setTimeout(function(){
                inInterval = false;
            }, intervalTime)
        }
        
    }
}

function scrollHandler(event){
    console.log(new Date(event.timeStamp));
}

document.addEventListener("scroll", throttling(scrollHandler,500), false);

节流的核心是管理一个布尔值开关变量(inInterval),在某种条件下切换它的true值和false值,而真正的事件处理函数只在这个开关变量的某一个值的时候才执行,从而达到节流的目的。
节流函数它的实现有很多种,多种就在于控制这个开关变量的值的条件,会不一样。在上面的例子里,我通过setTimeout的方式,间断性的来改变inInterval的值。

现在来详细分析一下上面的实现:

1: 第一次scroll事件触发的时候,scrollHandler就被立即执行了,这个是我个人的一个考虑,希望对于第一次的事件触发能马上有一个回馈给客户。

2: 但是当第一次执行完了之后,我们马上把"inInterval=true", 假如这时候第二次scroll触发,代码执行到 if(!inInterval),因为条件表达式为false,所以scrollHandler不会被立即执行。那可能之后的第三次,第四次。。。scroll事件触发的时候,inInterval都是true,都不会执行处理函数。

3: 我们之前在把inInterval设置为true之后,同时设置了一个timeout,在经过一定的时间(intervalTime)之后,inInterval会被设置为false; 假如在这之后马上又触发了一次scroll事件,代码走到if(!inInterval),条件为true,scrollHandler就可以被执行了。

但是上面的代码存在一个问题,假如我们最后一次的scroll事件,正好发生在这个循环时间内,那它就永远得不到执行了。这个可能会是一个隐藏的bug, 比方说你在进行一次拖拽事件,那目标元素可能永远都拖不到目的地。
所以我们要改一下代码,让最后一次事件能被执行:

function throttling(fn, intervalTime){
    let inInterval = false;
    let lastTimer = undefined;
    let timer = undefined;
    return function(){
        let context = this;
        let args = arguments;
        if(!inInterval) {
            clearTimeout(lastTimer); //这行代码很重要
            fn.apply(context, args);
            inInterval = true;
            timer = setTimeout(function(){
                inInterval = false;
            }, intervalTime)
        }else{
            clearTimeout(lastTimer);
            lastTimer = setTimeout(function(){
                fn.apply(fn, args);
                inInterval = false;
            }, intervalTime);
        }
        
    }
}

function scrollHandler(event){
    console.log(new Date(event.timeStamp));
}

document.addEventListener("click", throttling(scrollHandler,1000), false);

上面的代码,要特别解释一下一下这一行代码:clearTimeout(lastTimer); //这行代码很重要

如果我们不加这行代码的话,会出现先点击的click事件反而后执行的问题。比如;我们的intervalTime设置为10s, 然后我们分别在第0s, 第5秒,第12秒都进行一次点击,我们通过console.log(new Date(event.timeStamp)) 打印每一次事件发生时的时间, 我们会看到第5秒的那个click事件会比第12秒的那个click后输出,这就说明这里有问题。

所以我们要在if(!inInterval){}里面把lastTimer给清掉,也就是通过clearTimeout(lastTimer); 这行代码。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/83768.html

相关文章

  • 理解节流防抖

    摘要:节流节流限制了一个函数可以在短时间内被调用的次数。更新防抖防抖确保了一个函数只有在一个固定时间段内没有被调用过后,才会再次被调用。再换句话说防抖会等待事件不再高频发生,再触发。这个网站很好的可视化了节流与防抖。 节流 Throttling 节流限制了一个函数可以在短时间内被调用的次数。可以这样形容:在一毫秒内最多执行此函数 1 次。 Throttling enforces a maxi...

    glumes 评论0 收藏0
  • 高级函数技巧-函数防抖节流

    摘要:封装方法也比较简单,书中对此问题也进行了处理使用定时器,让函数延迟秒后执行,在此秒内,然后函数再次被调用,则删除上次的定时器,取消上次调用的队列任务,重新设置定时器。 在实际开发中,函数一定是最实用最频繁的一部分,无论是以函数为核心的函数式编程,还是更多人选择的面向对象式的编程,都会有函数的身影,所以对函数进行深入的研究是非常有必要的。 函数节流 比较直白的说,函数节流就是强制规定一...

    whinc 评论0 收藏0
  • Javascript 面试中经常被问到三个问题!

    摘要:相反,在讨论时,面试中通常会提到三件事。而认为最后一个参赛者说了算,只要还能吃的,就重新设定新的定时器。试想,如果用户的操作十分频繁他每次都不等设置的时间结束就进行下一次操作,于是每次都为该用户重新生成定时器,回调函数被延迟了不计其数次。本文不是讨论最新的 JavaScript 库、常见的开发实践或任何新的 ES6 函数。相反,在讨论 JavaScript 时,面试中通常会提到三件事。我自己...

    chnmagnus 评论0 收藏0
  • 3个经常被问到 JavaScript 面试题

    摘要:更高效的解决方案是将一个事件侦听器实际绑定到父容器上,然后在实际单击时可以访问每个确切元素。如果将事件侦听器绑定到窗口滚动事件上,并且用户快速滚动页面,事件很可能会在短时间多次触发。 原文链接 问题 #1: 事件委托 事件委托,也叫事件委派,事件代理。 当构建应用程序时,有时需要将事件监听器绑定到页面上的某些元素上,以便在用户与元素交互时执行某些操作。 假设我们现在有一个无序列表: ...

    Galence 评论0 收藏0
  • [译]通过实例讲解DebouncingThrotting(防抖节流)

    摘要:译通过实例讲解和防抖与节流源码中推荐的文章,为了学习英语,翻译了一下原文链接作者本文来自一位伦敦前端工程师的技术投稿。首次或立即你可能发现防抖事件在等待触发事件执行,直到事件都结束后它才执行。 [译]通过实例讲解Debouncing和Throtting(防抖与节流) lodash源码中推荐的文章,为了学习(英语),翻译了一下~ 原文链接 作者:DAVID CORBACHO 本文来自一位...

    Jenny_Tong 评论0 收藏0

发表评论

0条评论

Yujiaao

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<