wait函数_跟着Lodash大佬学编程思想 | 函数的防抖(debounce)和节流(throttle)

▲ 点击上方蓝字关注我 ▲

文 / 景朝霞

来源公号 / 朝霞的光影笔记

ID / zhaoxiajingjing

目录:0 / 应用场景(1)防抖he节流de概念(2)小技巧:标识判断(3)小技巧:按钮置灰,移除事件1 / 函数防抖 debounce(1)搭架子(2)设置定时器标识(3)设置立即执行(4)完整的:函数防抖debounce2 / 函数节流 throttle(1)搭架子(2)超过wait再次触发(3)wait时间内的定时器只需要设定一个(4)刚好遇见临界点(5)完整的:函数节流 throttle

6e9e8c9476c6575a94090728e37fb37a.png

————————第二天————————

816e31c39233eb66cad56a2a1c4e5c5a.png

0 / 应用场景

函数的防抖(debounce)和节流(throttle)

在 高频 触发的场景下,需要进行防抖和节流,比如:

① 狂点一个按钮 给你的爱豆投票的按钮

② 页面滚动 一下加载500+的数据,往下滚动的时候

③ 输入模糊匹配 百度的搜索条,每次输入都在触发搜索的接口

④ ……

html

<head>    <style>        * {            margin: 0;            padding: 0;        }        html,        body {            height: 300%;            background: -webkit-linear-gradient(top left, lightblue, orange, lightgreen);            background: linear-gradient(top left, lightblue, orange, lightgreen);        }style>head><body>    <button id="submit">点我呀~~~button>body>

△ html

JS代码

submit.onclick = function (){    // 当我狂点按钮时,就会一直输出"hello"    console.log('hello');};window.onscroll = function (){    // 默认情况下,页面滚动中:    // 浏览器在最快的反映时间内(4~6ms)    // 就会识别监听一次事件触发    // 把绑定的方法执行,    // 这样导致方法执行的次数过多    // 造成不必要的资源浪费    console.log('world');};

△ JS

(1)防抖he节流de概念

我们自己可以规定:多长时间内,触发2+次就算是“高频”触发了,那封装的方法,就可以指定这个频率

防抖特点:在某一次高频触发下,只识别一次,可以控制是在开始的时候触发,还是在最后一次触发

假装:咱规定500ms内触发多次都算是高频触发,只要我们检测到是高频触发了,则在本次频繁操作下也只触发一次

哪怕咱疯狂连续点击了10min,也只识别一次,因为每次点击的间隔都是小于500ms的

节流特点:在某一次高频触发下,不是只识别一次,按照我们设定的时间间隔(自己设定的频率),每到达这个频率都会触发一次。

假装:咱规定的频率是500ms,一直操作了10min,那么触发的次数:(10*60*1000)/500

一直狂点10min,每隔500ms识别一次

(2)小技巧:标识判断
let flag = false;submit.onclick = function (){    if(flag) return;    flag = true;    console.log('hello');    setTimeout(()=>{        flag=false;    }, 500);};

△ 在间隔多长时间后可以点

(3)小技巧:按钮置灰,移除事件
function handle(){    submit.onclick = null;    submit.disabled = true;    //...CODE    console.log('hello');    setTimeout(()=>{        submit.onclick = handle;        submit.disabled = false;    },500);}submit.onclick = handle;

△ 按钮置灰,移除事件

1 / 函数防抖 debounce

func[function]:最后要触发执行的函数

wait[number]:"频繁"设定的界限

immediate[boolean]:默认多次操作,咱识别的是最后一次,如果传参了immediate=true,让其识别第一次的

(1)搭架子
/** * @params *  func[function]:最后要触发执行的函数 *  wait[number]:"频繁"设定的界限 *  immediate[boolean]:默认多次操作,我们识别的是最后一次,但是immediate=true,让其识别第一次的 */function debounce(func, wait, immediate){    if(typeof func !== 'function') throw new TypeError('func must be a function');    if(typeof wait === 'undefined') wait = 500;    if(typeof wait === 'boolean') {        immediate = wait;        wait = 500;    }    if(typeof immediate !== 'boolean') immediate = false;        //...CODE        return function proxy(...args){        let self = this;//=> this被点击的按钮        func.call(self, ...args);    };}function handle(event){    console.log('hello');}submit.onclick = debounce(handle, 500, true);// sumbit.onclick = proxy;// submit.onclick = debounce(handle, true);// submit.onclick = debounce(handle);

△ 对于三个参数的处理默认值

sumbit.onclick = proxy; 疯狂点击按钮时,proxy函数会疯狂执行

在proxy中,会根据频率管控handle执行的次数

浏览器在4~6ms就会监听一次submit的点击事件,这个是咱没法拦着的。但,咱自己可以重构代理函数:柯理化函数

柯理化函数:预先存起一些东西,以后当我点击按钮时,才会去执行

闭包的应用

(2)设置定时器标识
function debounce(func, wait, immediate){    // ...CODE:默认值处理        return function proxy(...args){        let self = this;        // 如果这样写的话        // 在疯狂点击按钮时        // 每隔4~6ms会有一个定时器去排队        // 那么在间隔wait时间内,我点多少次        // 就会有每隔4~6ms,会有一个定时器去排队了        // 【此时,需要一个定时器的标识来控制不需要排队的清掉】        setTimeout(()=>{            func.call(self, ...args);        }, wait);    }}function handle(event){    console.log('hello');}submit.onclick = debounce(handle, 500, true);// submit.onclick = proxy;

△ 定时器

function debounce(func, wait, immediate){    // ...CODE:默认值处理        let timer = null;    return function proxy(...args){        let self = this;                clearTimeout(timer);        timer = setTimeout(()=>{            func.call(self, ...args);        }, wait);    }}function handle(){    console.log('hello');}submit.onclick = debounce(handle, 500, true);// submit.onclick = proxy;

△ 设置定时器标识

假装:咱规定在500ms内,如果有第二次点击事件触发了,那么,就是高频的

第一次proxy执行,设定了一个定时器

过了4ms,第二次proxy执行,需要清除前面的定时器,重新设定一个定时器

……

在500ms内,疯狂点击100次了,那么前面的99次设定的定时器都被清掉了,只留了最后一次的定时器

(3)设置立即执行
function debounce(func, wait, immediate){    // ...CODE:默认值处理        let timer = null;    return function proxy(...args){        let self = this,            now = immediate && !timer;                clearTimeout(timer);        timer = setTimeout(()=>{            timer = null;            !immediate ? func.call(self, ...args) : null;        }, wait);                //=> 第一次触发立即执行        now ? func.call(self, ...args) : null;    }}function handle(){    console.log('hello');}submit.onclick = debounce(handle, 500, true);// submit.onclick = proxy;

△ 设置immediate的参数了

immediate=true 设置了立即执行,那么定时器里面的就不用执行了

(4)完整的:函数防抖debounce
function debounce(func, wait, immediate){    if(typeof func !== 'function') throw new TypeError('func must be a function');    if(typeof wait === 'undefined') wait = 500;    if(typeof wait === 'boolean') {        immediate = wait;        wait = 500;    }    if(typeof immediate !== 'boolean') immediate = false;        let timer = null;    return function proxy(...args){        let self = this,            now = immediate && !timer;                clearTimeout(timer);        timer = setTimeout(()=>{            timer = null;            !immediate ? func.call(self, ...args) : null;        }, wait);                now ? func.call(self, ...args) : null;    }}

△ 函数防抖debounce

2 / 函数节流 throttle
(1)搭架子
/** * 函数防抖 * @param {function} func * @param {number} wait * @returns */function throttle(func, wait){    if(typeof func !== 'function') throw new TypeError('func must be a function');    if(typeof wait === 'undefined') wait = 500;    return function proxy(...args){        let self = this;    };}function handle(){    console.log('world');}window.onscroll = throttle(handle, 500);// window.onscroll = proxy;

△ 搭架子

(2)超过wait再次触发

c93a931986518b8f1a2a7a094ca394be.png

△ 图1_操作

function throttle(func, wait){    // ...CODE 默认值容错        let timer = null,        previous = 0; // 记录上一次操作的时间    return function proxy(...args){        let self = this,            now = new Date(),  // 当前这次触发的操作的时间            remaining = wait - (now - previous); // 两次的间隔时间        if(remaining <= 0) {            // 两次间隔时间超过wait了,直接执行即可            previous = now; // 下一次的开始时间            func.call(self, ...args);        } else {            // 两次触发间隔时间没有超过wait,则设置定时器,让其等待remaining这么久之后执行一次            timer = setTimeout(function (){                previous = new Date(); // 下一次开始的时间                func.call(self, ...args);            }, remaining);        }    };}function handle(){    console.log('world');}window.onscroll = throttle(handle, 500);// window.onscroll = proxy;

△ 第一次会立即执行一次

new Date() - 0 的值肯定是超过500ms的,第一次会立即执行一次

(3)wait时间内的定时器只需要设定一个
timer = setTimeout(....);

△ timer

① 给timer赋值,timer是一个数字,存储当前是第几个定时器

② clearTimeout(timer) 从系统中清除定时器,系统中没有定时器,但是timer的值还是那个数字

timer = null 重新赋值,可以基于timer是否为null了解到是否还有定时器

比如:小虾同学去银行要开卡,拿到一个排号101

....到柜台上,把业务办好了,需要在系统中删除这个号码

但是,纸条还在你手里

然后,柜台业务员会把你手里的纸片儿拿过去,撕掉

timer=null 就是把这个定时器撕掉了

e222d5342f49ebe270e2de97986cc628.png

△ 图2

function throttle(func, wait){    if(typeof func !== 'function') throw new TypeError('func must be a function');    if(typeof wait === 'undefined') wait = 500;    let timer = null,        previous = 0; // 记录上一次操作的时间    return function proxy(...args){        let self = this,            now = new Date(),  // 当前这次触发的操作的时间            remaining = wait - (now - previous); // 两次的间隔时间        if(remaining <= 0) {            // 两次间隔时间超过wait了,直接执行即可            previous = now; // 下一次的开始时间            func.call(self, ...args);        } else if(!timer){            // 两次触发间隔时间没有超过wait,则设置定时器,让其等待remaining这么久之后执行一次            // 【前提】没有设置过定时器            timer = setTimeout(function (){                clearTimeout(timer);                timer = null;                                previous = new Date(); // 下一次开始的时间                func.call(self, ...args);            }, remaining);        }    };}

△ 清掉定时器

d1841f64b10cbc0c5090f3bb49a463ba.png

△ 图3_1.清掉多余的定时器 2. 临界点,清掉定时器

(4)刚好遇见临界点
function throttle(func, wait){    if(typeof func !== 'function') throw new TypeError('func must be a function');    if(typeof wait === 'undefined') wait = 500;    let timer = null,        previous = 0; // 记录上一次操作的时间    return function proxy(...args){        let self = this,            now = new Date(),  // 当前这次触发的操作的时间            remaining = wait - (now - previous); // 两次的间隔时间        if(remaining <= 0) {            // 两次间隔时间超过wait了,直接执行即可            clearTimeout(timer);            timer = null;            previous = now; // 下一次的开始时间            func.call(self, ...args);        } else if(!timer){            // 两次触发间隔时间没有超过wait,则设置定时器,让其等待remaining这么久之后执行一次            // 【前提】没有设置过定时器            timer = setTimeout(function (){                clearTimeout(timer);                timer = null;                previous = new Date(); // 下一次开始的时间                func.call(self, ...args);            }, remaining);        }    };}

△ 1.清掉多余的定时器 2. 临界点,清掉定时器

(5)完整的:函数节流 throttle
function throttle(func, wait){    if(typeof func !== 'function') throw new TypeError('func must be a function');    if(typeof wait === 'undefined') wait = 500;    let timer = null,        previous = 0;    return function proxy(...args){        let self = this,            now = new Date(),              remaining = wait - (now - previous);         if(remaining <= 0) {            clearTimeout(timer);            timer = null;            previous = now;             func.call(self, ...args);        } else if(!timer){            timer = setTimeout(function (){                clearTimeout(timer);                timer = null;                previous = new Date();                 func.call(self, ...args);            }, remaining);        }    };}function handle(){    console.log('world');}window.onscroll = throttle(handle, 500);// window.onscroll = proxy;

△ 函数节流

- end -

lodash的debounce:https://github.com/lodash/lodash/blob/master/debounce.js

loadash的throttle:https://github.com/lodash/lodash/blob/master/throttle.js

1a17e2cd094661f42ebcc2fbfb6fefc7.png

从"你"到"更好的你",有无限可能~

4d448ad7e26d2c1030a34dd9c874b69a.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值