zoukankan      html  css  js  c++  java
  • “如何稀释scroll事件”的思考(不小心写了个异步do...while)

    看了下园友的一帖子:http://www.cnblogs.com/xzhang/p/4145697.html#commentform

    本来以为是很简单的问题,但仔细想想还挺有意思的。简单的说就是增加事件触发的间隔时间。

    比如在浏览器的事件是1毫秒调用一次,转换成100毫秒调用一次。

    看了下原贴的两方法,觉得可以乐观锁的方法再写个,虽然最后对比结果和typeahead差不多。但过程还是挺有意思的,分享下思路

    首先,浏览器事件间隔是没法改变的。所以我们只能改变回调函数的执行间隔。

    乐观锁机制的流程是:

                do {
                    var _oldVal = _nowVal//记录基准值
                    //执行任务
                } while (_oldVal != _nowVal)//比较 检查_nowVlau是否在执行任务的时候发生变化
    

      

    根据这个结构,基本的设计思路就是:

      触发事件时启动一个“任务线程”,执行完指定的任务后,隔一段时间去检查没有没有发生后续事件(通过_oldVal与_nowVal判断),如果有重新执行任务,没有结束线程。

    但有一个问题,“任务线程”同一时间只能执行一个,所以在触发事件的时候要做下判断。修改后的程序

        var _taskRun = false, _nowVal = 0, _oldVal = 0;
        function eventFun() {
            if (_taskRun) {
                _nowVal++;
            }
            else {
                _taskRun = true;
                do {
                    var _oldVal = _nowVal//记录基准值
                    //执行任务
                } while (_oldVal != _nowVal)//比较 检查_nowVlau是否在执行任务的时候发生变化
                _taskRun = false;//执行结束重置状态变量
                _nowVal = 0;
            }
        }
    

    一切很顺利,但是要有间隔啊!js里可没有Thread.sleep,但有setTimeout,所以可以用setTimeout模拟sleep  

    SO,Think.....了一会,不小心写了个异步do...while

    var _taskRun = false, _nowVal = 0, _oldVal = 0, _time = 100;
            var _do = function (waitTime, funTsk) {//模拟do{}while(true);
                var _endInner, _whileInner;
                _whileInner = function (funcondition) {
                    _endInner = function (funEnd) {
                        var _funWhile = function () {
                            if (funcondition()) {
                                _endInner(funEnd);
                            } else {
                                funEnd();
                            }
                        };
    
                        var _runInner = function () {
                            funTsk();
                            setTimeout(_funWhile, waitTime);//延迟一段时间做判断
                        };
                        _runInner();
                    };
                    return {
                        "end": _endInner
                    };
                };
                return { "while": _whileInner };
            };
    
    
            function eventFun() {
                if (_taskRun) {
                    _nowVal++;
                }
                else {
                    _taskRun = true;
    
                    _do(
                        100,//间隔时间
                         function () {
                             _oldVal = _nowVal//记录基准值
                             console.log(_oldVal);
                         }
                    )
                    .while(
                        function () {
                            return _oldVal != _nowVal
                        }
                    )
                    .end(
                        function () {
                            _taskRun = false;//执行结束重置状态变量
                            _nowVal = 0;
                        }
                    );
                }
            }
    

      

    现在,基本OK了,但做了下测试,发觉间隔时间没有typeahead的准,怎么回事?

    研究了下他的代码。发现高手的思路就不不一样。原来他是用当前时间去计算setTimeout的调用间隔的。SO出来的结果更加准确。

    比如,设置间隔100秒,一个事件在距上个事件50秒的时候发生,为了保证每次100秒的间隔,这个事件的setTimeOut时间就应该设置成50秒而不是100秒

    根据这个思路再修改了下代码,给出完整版:

        var _asynFun = function (func, wait) {
            var context, args, result, _taskRun = false, _nowVal = 0, _oldVal = 0;
            var _do = function (waitTime, funTsk) {//模拟do{}while(true);
                var _endInner, _whileInner;
                _whileInner = function (funcondition) {
                    _endInner = function (funEnd) {
                        var _funWhile = function () {
                            if (funcondition()) {
                                _endInner(funEnd);
                            } else {
                                funEnd();
                            }
                        };
                        var _runInner = function () {
    
                            var _previous = new Date();
                            result = funTsk.apply(context, args);
                            var _remaining = waitTime - ((new Date()) - _previous);
                            setTimeout(_funWhile, _remaining);//延迟一段时间做判断
                        };
                        _runInner();
                    };
                    return {
                        "end": _endInner
                    };
                };
                return { "while": _whileInner };
            };
    
    
            return function () {
                context = this;
                args = arguments;
                if (_taskRun) {
                    _nowVal++;
                }
                else {
                    _taskRun = true;
    
                    _do(
                        wait,//间隔时间
                         function () {
                             _oldVal = _nowVal//记录基准值
                             func();
                         }
                    )
                    .while(
                        function () {
                            return _oldVal != _nowVal
                        }
                    )
                    .end(
                        function () {
                            _taskRun = false;//执行结束重置状态变量
                            _nowVal = 0;
                        }
                    );
                }
                return result;
            }
        }
    

      

    本文版权归作者和博客园共有,未经作者本人同意禁止任何形式的转载,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。

    最后再贴出测试代码:

    <html>
    <head>
        <title>
            “如何稀释scroll事件”测试
        </title>
        <meta charset="utf-8">
    </head>
    <body>
        <div style="margin:auto;600px;padding:20px">
            <input id="waitTime" type="text" value="100" onchange="onscrollTest()" style="float:left" />
            <select id="sel" onchange="onscrollTest()" style="float:left">
                <option value="1">使用_lazyRun</option>
                <option value="2">使用debounce</option>
                <option value="3">使用throttle </option>
                <option value="4">使用_asynFun </option>
            </select>
            <div id="outDiv" style="float:left"></div>
    
        </div>
        <div style="auto;text-align:right;font-weight:bold;color:red;padding:10px;font-size:20px">滚动条----------------------------------------------></div>
        <div id="box" style="auto; height:200px;overflow-y:scroll;">
            <div style="border-color:goldenrod;border: 2px solid #f60;height:6000px;auto;background-color:goldenrod"></div>
        </div>
    </body>
    </html>
    
    
    <script type="text/javascript">
    
    
        var _lazyRun = function (func, wait) {
            var _preIndex = 0, _nowIndex = 1, _timer, _fnCur, _context, _result;
            var _fn1 = function () {
                if (_preIndex < _nowIndex) {
                    var _previous = new Date();
                    _fnCur = _fn2;
                    clearTimeout(_timer);
                    _preIndex = _nowIndex;
                    _result = func.apply(_context, _args);
                    var _remaining = wait - ((new Date()) - _previous);
                    if (_remaining < 0) {
                        _result = _fn1.apply(_context, _args);
                    } else {
                        _timer = setTimeout(_fn1, _remaining);//脱离线程
                    }
                } else {
                    _fnCur = _fn1;
                    _preIndex = 0, _nowIndex = 1;
                }
                return _result;
    
            };
            var _fn2 = function () {
                _nowIndex++;
                return _result;
            };
            _fnCur = _fn1;
            return function () {
                _context = this;
                _args = arguments;
                _result = _fnCur.apply(_context, _args);
                return _result;
            };
        };
    
    
        //**************************underscore.js 的 debounce
        /**
        * [debounce description]
        * @param  {[type]} func      [回调函数]
        * @param  {[type]} wait      [等待时长]
        * @param  {[type]} immediate [是否立即执行]
        * @return {[type]}           [description]
        */
        var _ = {};
        _.debounce = function (func, wait, immediate) {
            var timeout, args, context, timestamp, result;
    
            var later = function () {
                var last = _.now() - timestamp;
    
                //小于wait时间,继续延迟wait-last执行later,知道last >= wait才执行func
                if (last < wait && last > 0) {
                    timeout = setTimeout(later, wait - last);
                } else {
                    timeout = null;
                    if (!immediate) {
                        result = func.apply(context, args);
    
                        if (!timeout) context = args = null;
                    }
                }
            };
    
            return function () {
                context = this;
                args = arguments;
                timestamp = _.now();
                //是否立即执行
                var callNow = immediate && !timeout;
    
                if (!timeout) timeout = setTimeout(later, wait);
    
                if (callNow) {
                    result = func.apply(context, args);
                    context = args = null;
                }
    
                return result;
            };
        };
    
        _.now = Date.now || function () {
            return new Date().getTime();
        };
    
        //**************************typeahead.js 的 throttle
        var throttle = function (func, wait) {
            var context, args, timeout, result, previous, later;
            previous = 0;
            later = function () {
                previous = new Date();
                timeout = null;
                result = func.apply(context, args);
            };
            return function () {
                var now = new Date(),
                    remaining = wait - (now - previous);
                context = this;
                args = arguments;
                if (remaining <= 0) {   //如果大于间隔时间(wait)
                    clearTimeout(timeout);
                    timeout = null;
                    previous = now;
                    result = func.apply(context, args);
                } else if (!timeout) {  //小于,延时调用later
                    timeout = setTimeout(later, remaining);
                }
                return result;
            };
        };
    
    
    
        ///导步do{}while
        var _asynFun = function (func, wait) {
            var context, args, result, _taskRun = false, _nowVal = 0, _oldVal = 0;
            var _do = function (waitTime, funTsk) {//模拟do{}while(condition);
                var _endInner, _whileInner;
                _whileInner = function (funcondition) {
                    _endInner = function (funEnd) {
                        var _funWhile = function () {
                            if (funcondition()) {
                                _endInner(funEnd);
                            } else {
                                funEnd();
                            }
                        };
                        var _runInner = function () {
                            var _previous = new Date();
                            result = funTsk.apply(context, args);
                            var _remaining = waitTime - ((new Date()) - _previous);
                            setTimeout(_funWhile, _remaining);//延迟一段时间做判断
                        };
                        _runInner();
                    };
                    return {
                        "end": _endInner
                    };
                };
                return { "while": _whileInner };
            };
    
    
            return function () {
                context = this;
                args = arguments;
                if (_taskRun) {
                    _nowVal++;
                }
                else {
                    _taskRun = true;
    
                    _do(
                        wait,//间隔时间
                         function () {
                             _oldVal = _nowVal//记录基准值
                             func();
                         }
                    )
                    .while(
                        function () {
                            return _oldVal != _nowVal
                        }
                    )
                    .end(
                        function () {
                            _taskRun = false;//执行结束重置状态变量
                            _nowVal = 0;
                        }
                    );
                }
                return result;
            }
        }
    
    
        //**************************测试***********************************
        var _testCount = 0;
        var _test = function () {
            console.log(_.now())
            _testCount++;
            //console.log(window.scrollY || document.documentElement.scrollTop);
    
        };
        function onscrollTest() {
            _testCount = 0;
            var _waitTime = document.getElementById("waitTime").value;
            $box = document.getElementById("box");
            switch (document.getElementById("sel").value) {
                case "1"://_lazyRun
                    document.getElementById("outDiv").innerText = "use _lazyRun function ,wait time is " + _waitTime;
                    $box.onscroll = _lazyRun(_test, _waitTime);
                    break;
                case "2"://debounce
                    document.getElementById("outDiv").innerText = "use debounce function ,wait time is " + _waitTime;
                    $box.onscroll = _.debounce(_test, _waitTime);
                    break;
                case "3"://throttle
    
                    document.getElementById("outDiv").innerText = "use throttle function ,wait time is " + _waitTime;
                    $box.onscroll = throttle(_test, _waitTime);
                    break;
                case "4"://throttle
    
                    document.getElementById("outDiv").innerText = "use _asynFun function ,wait time is " + _waitTime;
                    $box.onscroll = _asynFun(_test, _waitTime);
                    break;
            };
            console.clear();
    
        }
        onscrollTest();
    </script>
    测试页面HTML
  • 相关阅读:
    php && 逻辑与运算符使用说明
    php无穷递归算法
    PHP foreach 用法
    centos安装g++
    php 编译中apxs
    shutdown()
    C语言strtok()函数:字符串分割
    细谈select函数(C语言)
    setsockopt的作用
    STL之七:STL各种容器的使用时机详解
  • 原文地址:https://www.cnblogs.com/Grart/p/4148383.html
Copyright © 2011-2022 走看看