现在的位置: 首页 > Tools > underscore > 正文
Underscore之throttle函数源码分析以及使用注意事项
2015年04月02日 underscore ⁄ 共 3041字 评论数 2 ⁄ 被围观 3,665 views+

Underscore提供了一些提升界面性能的函数,throttle函数就是其中之一,这个函数的从字面意义上不难理解,是用于限制用户函数调用频率的。但是细节上还是有不少地方需要注意的。

_.throttle(function, wait, [options])

以下这段是引用自官网的API和中文API的翻译

Creates and returns a new, throttled version of the passed function, that, when invoked repeatedly, will only actually call the original function at most once per every waitmilliseconds. Useful for rate-limiting events that occur faster than you can keep up with.

By default, throttle will execute the function as soon as you call it for the first time, and, if you call it again any number of times during the wait period, as soon as that period is over. If you'd like to disable the leading-edge call, pass {leading: false}, and if you'd like to disable the execution on the trailing-edge, pass {trailing: false}.

创建并返回一个像节流阀一样的函数rtnFun,当以一定频率重复调用rtnFun函数的时候,rtnFun函数可以保证“最多每隔 wait毫秒调用一次function函数”。对于想控制一些触发频率较高的事件有帮助。

默认情况下,throttle将在你调用的第一时间尽快执行function,并且,如果你在wait周期内调用任意次数的函数,都将尽快的被覆盖。如果你想禁用第一次首先执行的话,传递{leading: false},还有如果你想禁用最后一次执行的话,传递{trailing: false}

关于第三个参数[options],个人通过测试和源码的梳理,觉得中文API的翻译表达的并不准确,或者说比较模糊。其实leading和trailing是分别对应两种模式,即:“及时执行模式”和“延时执行模式”,而他们的组合有四种情况。

及时模式

{leading:true,trailing:false} 对应“及时执行模式”,也就是说function每次被调用到的时候,都是立即执行的。function的调用跟rtnFun的调用是顺序同步的。

延时模式

{leading:false,trailing:true} 对应“延时执行模式”,也就是说function每次被调用到的时候,其实都是通过setTimeout执行的,function的调用跟rtnFun的调用是异步的。

全关模式

{leading:false,trailing:false} 这种都关闭的情况,存在不合理的地方。

在第一批次重复调用rtnFun时(wait时间段内),function一次都不会被调用。比如说,新打开的页面上有个按钮,你快速点了几下,结果什么也没触发,过了一会儿,你再点下,才有函数被触发,这显然是个Bug。

全开模式

{leading:true,trailing:true} 这种都开启的情况,也存在不合理的地方。

重复调用rtnFun时(wait时间段内),function经常被触发两次(两次时间间隔大于等于wait),一次是“及时执行模式”的调用,另一次是“延时执行模式”,尽管function这两次调用的时间间隔大于等于wait。一般情况下,这并不是我们想要的结果,我们只想function被调用一次。

增对这四种情形,我做了一个Demo,仔细体会的话,可以很明显的看出区别:

http://www.easyui.info/third/underscore/throttle.html

源码分析:

  1. function throttle(func, wait, options) {
  2.     // 上下文,函数参数,函数返回值
  3.     var context, args, result;
  4.     // 延时器
  5.     var timeout = null;
  6.     // 上一次执行的func的时间点
  7.     var previous = 0;
  8.     if (!options) options = {};
  9.     // 延时执行函数
  10.     var later = function() {
  11.         // 如果及时调用被关闭,则设置previous为0
  12.         previous = options.leading === false ? 0 : _.now();
  13.         timeout = null;
  14.         result = func.apply(context, args);
  15.         if (!timeout) context = args = null;
  16.     };
  17.     /** 以上变量以及函数都是通过闭包的方式访问的 **/
  18.     return function() {
  19.         var now = _.now();
  20.         if (!previous && options.leading === false) previous = now;
  21.         // remaining容易理解,表示还剩多少时间可以再次执行func
  22.         var remaining = wait - (now - previous);
  23.         // 保存上下文
  24.         context = this;
  25.         // 获取函数参数
  26.         args = arguments;
  27.         // 及时模式
  28.         // remaining小于等于0是跳出wait的限制,可以执行了
  29.         // remaining大于wait的情况,只有在客户机修改了系统时间的时候才会出现
  30.         // 这两种情况都可以立刻对func做调用
  31.         if (remaining <= 0 || remaining > wait) {
  32.             // 清除定时器
  33.             if (timeout) {
  34.                 clearTimeout(timeout);
  35.                 timeout = null;
  36.             }
  37.             previous = now;
  38.             result = func.apply(context, args);
  39.             if (!timeout) context = args = null;
  40.         } else if (!timeout && options.trailing !== false) {// 延时模式
  41.             timeout = setTimeout(later, remaining);
  42.         }
  43.         return result;
  44.     };
  45. }

目前有 2 条留言 其中:访客:0 条, 博主:0 条 引用: 2

    给我留言

    留言无头像?


    ×