最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 事件防抖引发的对事件循环机制的思考

    正文概述 掘金(鲍鲍18)   2021-07-27   646

    文章背景:

    • 事件防抖引发的对事件循环机制的思考,部分异步放在闭包外部的执行顺序
    • 代码实战:React 事件处理函数中 onClick = 直接调用函数 "this.fn()",或在调用时使用 bind绑定this "this.fn.bind(this)"、或在调用的时候使用箭头函数绑定this "() => this.handleClick()"三者区别

    先说结论

    防抖中,部分异步放在闭包外面,会导致结果如下:

    
    ...
    
    // 部分异步不防抖
        _this.props.dispatch({
            type: "test/debounce",
            param
        })
        .then(() => {
            _this.editData()
        })
        // 部分异步不防抖 end
        .then(() => {
            return function () {
                console.log('---第一次 timeout: ', timeout)  // 2 2 
                clearTimeout(timeout);  //每次触发input值改变时,首先清空上一次的setTimeout
                console.log('---第二次 timeout: ', timeout);  // 3 3
                timeout = setTimeout(() => { // 4 4
                        _this.updateAge()   // 重新获取页面数据
                    console.log('---我是setTimeout, 防抖清除函数们', "---timeout的值: ", timeout)
                }, 1000) // 注意的是如果你写的代码用有用到this,要提前声明this是谁,因为在箭头函数中没有this指向
                console.log('---debounce中的闭包执行完毕', timeout)
            }()
        })
        ...
        
        
    
    • 先去执行完同步代码(即两次点击后),setTimeout最后才会去执行;
    • 由于clearTimeout时,setTimeout并未执行,timeout并没有定义上setTimeout,所以其实并未清除 this.updateAge
    • setTimeout 最终执行的次数即为点击的次数


    作者:林轩
    链接:juejin.cn/post/698919… 来源:掘金
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    写法一 —— 正确写法

    第一步:render 中的定义click方法

    • 下方代码中方式一:不可行;

        因为bind是改变this,返回的一个函数,会将 函数debounce 改变下this,直接原封不动的返回;

        而我们要的是 debounce()执行一下,需要让 onClick = debounce(),返回一个闭包,最终我们执行的是 debounce中的返回的函数

    • 下方代码中方式二:可行;

        debounce(),返回的是里面定义的闭包;

        render时,会执行 debounce(),不过没啥太大关系,我们主要执行的是 debounce()里返回的函数,并且 debounce函数定义前面没太多与逻辑相关的代码,不会出错的

    // render
    // 方式一
    {/* <span className={styles.left} onClick={this.debounce.bind(this, 'subtract')}>-</span>  */}  
    
    // 方式二
    <span className={styles.left} onClick={this.debounce('subtract')}>-</span> 
    
    

    第二步:定义debounce 事件

    • 方法一:把所有函数写到setTimeout里面,可行

        正确合理使用闭包,清除函数是要清除所有的函数,而不能在外边 —— 例写法二中的debounce 定义

    debounce = (option) => {
        console.log('---进入debounce方法')
        let param={};
        //  处理一些逻辑
        if (option === 'str') {
            param = {a:1}
        } else {
            param = {a:1}
        }
    
        const _this = this;
        let timeout = null;
        return function () {
            console.log('---第一次 timeout: ', timeout)
            clearTimeout(timeout);  
            console.log('---第二次 timeout: ', timeout)
            timeout = setTimeout(() => { // 4 4
                **// 防抖的函数们** 
                _this.props.dispatch({
                    type: "test/debounce",
                    param
                })
                .then(() => {
                    _this.editData()
                })
                .then(() => {
                    _this.updateAge()
                })
                **// 防抖的函数们 end** 
    
                console.log('---我是setTimeout, 防抖清除函数们', "---timeout的值: ", timeout)
            }, 500) 
            console.log('---debounce中的闭包执行完毕', timeout)
        }
    }
    
    • 下方为快速点击两次按钮时,执行顺序如下:

    事件防抖引发的对事件循环机制的思考

    写法二 —— 错误写法(部分异步写到闭包外面,只想清除部分函数抖动)

    第一步:render定义

    • 由于写法二的错误写法,是把部分异步写到闭包外面,只想清除部分函数抖动,

    • render时,debounce() 中的异步们就会执行,可能某些数据还会更新好,render时就执行异步可能会导致报错

    • bind(),表示改变this,返回一个函数体,而不是执行函数;就不会导致render时,debounce执行了,而是onClick的时候去执行 debounce

    • 只能如下写,两种写法,这两种写法区别详见:react.docschina.org/docs/handli…

    <span className={styles.left} onClick={this.debounce.bind(this, 'subtract')}>-</span> 
    

    第二步:定义 debounce

    • 把部分异步写到闭包外面,不防抖;只想清除部分函数抖动,

    • 执行结果看下图,原因见下方的 "结论"

    // 加减年龄
    debounce = (option) => {
        console.log('---进入debounce方法')
        let param={};
        //  处理一些逻辑
        if (option === 'str') {
            param = {a:1}
        } else {
            param = {a:1}
        }
    
        const _this = this;
        let timeout = null;
    
        // 部分异步不防抖
        _this.props.dispatch({
            type: "test/debounce",
            param
        })
        .then(() => {
            _this.editData()
        })
        // 部分异步不防抖 end
        .then(() => {
            return function () {
                console.log('---第一次 timeout: ', timeout)  // 2 2 
                clearTimeout(timeout);
                console.log('---第二次 timeout: ', timeout);  // 3 3
                timeout = setTimeout(() => { // 4 4
                    _this.updateAge()
                    console.log('---我是setTimeout, 防抖清除函数们', "---timeout的值: ", timeout)
                }, 1000)
                console.log('---debounce中的闭包执行完毕', timeout)
            }()
        })
    
    }
    

    事件防抖引发的对事件循环机制的思考

    错误写法执行结果的原因

    • 因为事件循环机制是先 主任务 —> 微任务 —> 宏任务,setTimeout 为宏任务,将 需要在下一个时间周期执行

    • 事件循环机制流程图如下:

    事件防抖引发的对事件循环机制的思考

    • 详解如下:

    2 同步任务就继续执行,一直执行完 3 遇到异步API就将它交给对应的异步线程,自己继续执行同步任务 4 异步线程执行异步API,执行完后,将异步回调事件放入事件队列上 5 主线程手上的同步任务干完后就来事件队列看看有没有任务 6 主线程发现事件队列有任务,就取出里面的任务执行 7 主线程不断循环上述流程

    定时器不准

    Event Loop的这个流程里面其实还是隐藏了一些坑的,最典型的问题就是总是先执行同步任务,然后再执行事件队列里面的回调。这个特性就直接影响了定时器的执行,我们想想我们开始那个2秒定时器的执行流程:

    2 遇到setTimeout,将它交给定时器线程 3 定时器线程开始计时,2秒到了通知事件触发线程 4 事件触发线程将定时器回调放入事件队列,异步流程到此结束 5 主线程如果有空,将定时器回调拿出来执行,如果没空这个回调就一直放在队列里。

    结论

    上述错误写法的执行结果,结论如下:

    • 先去执行完同步代码(即两次点击后),setTimeout最后才会去执行;

    • 由于clearTimeout时,setTimeout并未执行,timeout并没有定义上setTimeout,所以其实并未清除 this.updateAge

    • setTimeout 最终执行的次数即为点击的次数

    举个例子:原理同上

    <!DOCTYPE html>
    <html>
    
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    
    <body>
        <div></div>
        <input id="inp" />
        <script>
            inp.addEventListener('input', () => {
                console.log('script start');
                setTimeout(function () {
                    console.log('---setTimeout')
                }, 5000);
    
                Promise.resolve().then(function () {
                    console.log('promise1');
                }).then(function () {
                    console.log('promise2');
                })
            });
        </script>
    </body>
    
    </html>
    

    打印如下:

    事件防抖引发的对事件循环机制的思考


    起源地下载网 » 事件防抖引发的对事件循环机制的思考

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元