“如何稀释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;width: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="width:auto;text-align:right;font-weight:bold;color:red;padding:10px;font-size:20px">滚动条----------------------------------------------></div>

    <div id="box" style="width:auto; height:200px;overflow-y:scroll;">

        <div style="border-color:goldenrod;border: 2px solid #f60;height:6000px;width: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

 

你可能感兴趣的:(scroll)