看了下园友的一帖子: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事件”的思考(不小心写了个异步do...while)的更多相关文章

  1. “如何稀释scroll事件”引出的问题

    背景:我在segmentfault提了个问题如何稀释onscroll事件,问题如下: 面试时问到这个问题,是这样的:    面试官问一个关于滚动到某个位置的时候出现一个顶部的导航栏,答完之后,她接着问 ...

  2. 如何提高scroll事件的性能

    1. chrome devtool 是诊断页面滚动性能的有效工具 2. 提升滚动时性能,就是要达到fps高且稳. 3. 具体可以从以下方面着手 使用web worker分离无页面渲染无关的逻辑计算 触 ...

  3. jQuery scroll事件实现监控滚动条分页示例(转)

    这篇文章主要介绍了jQuery scroll事件实现监控滚动条分页简单示例,使用ajax加载,同时介绍了(document).height()与$(window).height()的区别,需要的朋友可 ...

  4. scroll事件实现监控滚动条并分页显示示例(zepto.js)

    scroll事件实现监控滚动条并分页显示示例(zepto.js  ) 需求:在APP落地页上的底部位置显示此前其他用户的购买记录,要求此div盒子只显示3条半,但一页有10条,div内的滑动条滑到一页 ...

  5. JQUERY 滚动 scroll事件老忘记 标记下

    制作笔记 这个scroll事件 老忘记.... 写的太垃圾了  希望有路过的大神指点的吧~ 这个貌似应该写个函数里 调用好些的吧~  写个类这样的 也方便扩展貌似  不过就是想想  ~ $(windo ...

  6. jQuery scroll事件

    scroll事件适用于window对象,但也可滚动iframe框架与CSS overflow属性设置为scroll的元素. $(document).ready(function () { //本人习惯 ...

  7. Android ScrollView 嵌套 ListView、 ListView 嵌套ScrollView Scroll事件冲突解决办法

    本人菜鸟一名,最近工作了,开始学习Android. 最近在做项目的时候,UX给了个design,大概就是下拉刷新的ListView中嵌套了ScrollView,而且还要在ScrollView中添加动画 ...

  8. 前端资讯周报 3.13 - 3.19: WebVR来了!以及如何优化scroll事件性能

    每周一我都会分享上一周我订阅的技术站点中,和解决问题的过程中阅读到的值得分享的文章.这是迫使我学习的一个动力 本周推荐 Minecraft in WebVR with HTML Using A-Fra ...

  9. 关于如何绑定Jquery 的scroll事件(兼容浏览器 Wookmark瀑布流插件)

    做一个随屏幕滚动的导航条时,发现一个问题: 火狐.谷歌.ie9正常,ie8.7.6页面滚动时,导航条没有反应. 代码如下: $(document).bind("scroll",fu ...

随机推荐

  1. CSS下拉列表错误纠正

    上一篇关于CSS制作下来列表的错误纠正. 在上一篇中,用CSS只做了下拉列表,但是鼠标不放在导航栏上的时候,下拉列表也是出来的.具体错误就是 div ul{ list-style:none; max- ...

  2. JS实现验证码倒计时效果

    通常做注册页面时会有获取验证码按钮,然后点击后过60秒才能重新获取,比如现在项目中遇到的 然后点击后的样式,并且数字是递减的,到0时重新回到最初的状态(上图). 首先构造HTML结构 <butt ...

  3. C++ 11学习和掌握 ——《深入理解C++ 11:C++11新特性解析和应用》读书笔记(一)

    因为偶然的机会,在图书馆看到<深入理解C++ 11:C++11新特性解析和应用>这本书,大致扫下,受益匪浅,就果断借出来,对于其中的部分内容进行详读并亲自编程测试相关代码,也就有了整理写出 ...

  4. event事件对象和clientX,clientY

    一.event : 事件对象 , 当一个事件发生的时候,和当前这个对象发生的这个事件有关的一些详细的信息都会被临时保存到一个指定地方-event对象,供我们在需要的调用.如:飞机-黑匣子 事件对象必须 ...

  5. Image放大缩小在放进Imageview

    // 拿到要缩小放大的Bitmap obitmap = BitmapFactory.decodeResource(this.getResources(),R.drawable.ic_launcher) ...

  6. (转)assert 断言式编程

    编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,可以将断言看作是异常处理的一种高级形式.断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真.可以在任何时候启用 ...

  7. Linux文件权限查看及修改命令chmod

    查看权限 Linux文件访问权限分为可读,可写和可执行三种. 可用ls -l命令查看,例: ls -l或者 ll 显示为 -r--r--r--. 1 root root 21 Jan 5 23:02 ...

  8. spring 驱动模式

    驱动模式 public class ProAction extends ActionSupport implements ModelDriven<Product>{ //驱动模式 priv ...

  9. Virtualbox安装USB2.0/3.0

    系统:Ubuntu16.04 软件:Virtualbox5.1 1.打开Virtualbox,不启动虚拟系统. 2.点击设置->USB->启动usb2.0. 3.若发现不能启用,则到官网下 ...

  10. js组合继承和寄生组合式继承比较

    本文是原创文章,如需转载,请注明文章出处 1.js中实现组合继承(B继承A): function A(name){ this.name = name; this.ary = ["AA&quo ...