使用限制函数执行频率的函数代理
2014-08-15 18:04
375 查看
使用限制函数执行频率的函数代理
假设一个经典的CURD页面上,要做一个Ajax异步查询功能。放一个查询按钮,点击查询,系统会到远程服务端请求数据,一秒之后返回查询结果。
很快,功能实现了!
但假如用户一秒内点击了三次查询,会发生什么?
为了解决这个问题,我们可能会在用户点击查询之后禁用查询按钮,或者在处理查询时上锁,返回结果后再把锁放开。
很好,做到这里,已足够日常使用。
这里只解决了一个问题:按钮的点击。而输入框的输入、选择框的变化、鼠标的移动、滚轮的滚动,这些事件触发频率高的问题怎么解决?
为了综合考虑,不重复自己,一个解决方案诞生了:使用限制函数执行频率的函数代理。
该函数API看起来跟setTimeout以及Promise很有点像,确实,但在形式上略有差异。
打算把它推荐给ES6,可惜前段时间给es-discuss讨论组发了好几次的String Padding API邮件都被防火墙拦回来了。
唉!只能在博客中说说。
函数
/** * 为函数创建一个带帧速控制的代理函数 * 包含末尾的细节处理 * 注:此函数适用于调用间隔大于1ms的情形 * @static * @method getFPSLimitedFunction * @param {Function} accept - 指定的函数 * @param {Number} [fps=0] - 每秒最多执行的次数。(设interval为时间间隔毫秒数,该值等效于1000/interval) * @param {Function} [reject=null] - 拒绝执行时的回调。 * @returns {Function} agent - 代理函数 * @throws {TypeError} called_non_callable - 若accept不是function时则抛出该错误 */ function getFPSLimitedFunction(accept,reject,fps){ if(typeof accept!=="function"){ throw new TypeError(accept+" is not a function"); } if(typeof reject!=="function"){ reject=null; } fps>>>=0; var delay=Math.max(0,1000/fps), locked=false, timer=0, rejectedQueue=[], lastAcceptedTime=0, lastRejectedTime=0; var lock=function(){ locked=true; }; var unlock=function(){ locked=false; clearTimeout(timer); timer=setTimeout(checkRejectedCalls,delay); }; var checkRejectedCalls=function(){ if(lastAcceptedTime<lastRejectedTime){ var l=rejectedQueue.length, i, call; if(l>0){ if(typeof reject==="function"){ for(i=0;i<l-1;i++){ call=rejectedQueue[i]; try{ reject.apply(call[0],call[1]); }catch(e){ setTimeout(function(){throw e;},0); } } } call=rejectedQueue[l-1]; try{ accept.apply(call[0],call[1]); }catch(e){ setTimeout(function(){throw e;},0); } } } rejectedQueue.length=0; }; var handleRejectedCalls=function(){ if(typeof reject==="function"){ var l=rejectedQueue.length, i, call; for(i=0;i<l;i++){ call=rejectedQueue[i]; try{ reject.apply(call[0],call[1]); }catch(e){ setTimeout(function(){throw e;},0); } } } rejectedQueue.length=0; }; var agent=function(){ if(locked){ lastRejectedTime=Date.now(); rejectedQueue.push([this,arguments]); return; } clearTimeout(timer); handleRejectedCalls(); lock(); lastAcceptedTime=Date.now(); accept.apply(this,arguments); setTimeout(unlock,delay); }; agent.toString=function(){return accept.toString();}; if(accept.toSource){ agent.toSource=function(){return accept.toSource();}; } return agent; }
用例
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // demo1 限制事件监听函数调用频率 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //为document添加鼠标移动事件监听,监听每秒最多执行25次 var document_mousemoveHandler=getFPSLimitedFunction(function(event){ //TODO 处理鼠标移动 },null,25); document.addEventListener("mousemove",document_mousemoveHandler); //为查询按钮添加点击事件监听,监听每秒最多执行1次 var button_clickHandler=getFPSLimitedFunction(function(event){ //TODO 查询数据 },null,1); document.querySelector("#searchButton").addEventListener("click",button_clickHandler); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // demo2: 为document添加鼠标移动事件监听,监听每秒最多执行60次 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //demo2 function doSomethingWithIntervalAndTimes(something,interval,times){ var count=0; var timer=setInterval(function(){ count++; if(count>=times){ clearTimeout(timer); } something(count,times); },interval); return timer; } //1秒最多接受一次 var showMessage=getFPSLimitedFunction( function accept(s){//接受业务 console.log("accepted: "+s); }, function reject(s){//处理被拒绝 console.warn("rejected: "+s); }, 1 ); //每秒隔100ms一次(10fps),理论上每秒1次被接受,9次被拒绝 doSomethingWithIntervalAndTimes(function(currentCount,repeatCount){ showMessage(currentCount); },100,100);
相关文章推荐
- python使用装饰器和线程限制函数执行时间的方法
- Python使用装饰器和线程限制函数执行时间的方法
- 服务器控件customvalidator可以在前台执行脚本判断,不过要使用固定的函数
- 使用函数传递参数来执行相应的数据库操作
- C# 使用APM执行受I/O限制的异步操作(转)
- 【转载】使用动态代理记录方法执行的时间
- 使用Intel编译器(5)PGO(3)评测函数或循环执行时间(Profile Function or Loop Execution Time)
- 使用VB生成没有限制的函数DLL zz
- CSS执行JS表达式或函数 CSS中expression使用简介
- 很不错的使用频率比较高的JS函数
- PHP执行linux系统命令的常用函数使用说明
- bind 函数 - 使用闭包保存执行上下文
- 如何使用定时器settimeout、setInterval执行能传递参数的函数
- 使用动态代理记录方法执行的时间
- 使用动态代理记录方法执行的时间
- 如何使用定时器settimeout、setInterval执行能传递参数的函数
- 使用函数传递参数来执行数据库操作
- 关于:“无法序列化会话状态。在“StateServer”或“SQLServer”模式下,ASP.NET 将序列化会话状态对象,因此不允许使用无法序列化的对象或 MarshalByRef 对象。如果自定义会话状态存储在“Custom”模式下执行了类似的序列化,则适用同样的限制。”的问题
- Python: 使用装饰器“@”取得函数执行时间
- 客户端代理使用细节【函数调用/回调函数的完整签名及WebService级别默认属性】