跟着 underscore 学节流
更多内容请参考:我的新博客
在上一篇文章中,我们了解了为什么要限制事件的频繁触发,以及如何做限制:
- debounce 防抖
- throttle 节流
上次已经说过防抖的实现了,今天主要来说一下节流的实现。
节流
节流的原理很简单:
如果你持续触发事件,每隔一段时间,只执行一次事件。
根据首次是否执行已经结束后知否执行,效果有所不同,实现的方式也有所不同。
我们用leading代表首次是否执行,trailing 代表结束后是否再执行一次。
关于节流的实现,有两种主流的实现方式,一种是使用时间戳,一种是设置定时器。
使用时间戳
让我们来看第一种方法:使用时间戳,当触发事件的时候,我们取出当前的时间戳,然后减去之前的时间戳(最一开始值设为0),如果大于设置的时间周期,就执行函数,然后更新时间戳为当前的时间戳,如果小于,就不执行。
看了这个表述,让我们来写第一版的代码:
function throttle(func, wait) {
var context,args;
var previous = 0;
return function() {
var now = +new Date();
context = this;
args = arguments;
if(now - previous > wait) {
func.apply(context,args);
previous = now;
}
}
}
例子依然是用讲 debounce 中的例子,如果你要使用:
container.onmousemove = throttle(getUserAction,1000)
效果演示如下:
我们可以看到:当鼠标移入的时候,事件立即执行,每过1s 会执行一次,如果再 4.2s 停止触发,以后不会再执行事件。
使用定时器
接下来,我们讲讲第二种实现方式,使用定时器。
当触发事件的时候,我们设置一个定时器,再触发事件的时候,如果定时器存在,就不执行,直到定时器执行,然后执行函数,清空定时器,这样就可以设置下个定时器。
function throttle(func, wait) {
var timeout;
var previous = 0;
return function(){
context = this;
args = arguments;
if(!timeout){
timeout = setTimeout(function(){
timeout = null;
func.apply(context, args)
},wait)
}
}
}
为了让效果更加明显,我们设置wait 的时间为 3s,效果演示如下:
我们可以看到:当鼠标移入的时候,事件不会立刻执行,晃了 3s 后终于执行了一次,此后每 3s 执行以下,当数字显示为 3 的时候,立刻移除鼠标,相当于大约 9.2s 的时候停止触发,但是依然会在第 12s 的时候执行一次事件。
所以比较两个方法:
- 第一种事件会立刻执行,第二种事件会在 n 秒后第一次执行
- 第一种事件停止触发后没有办法再执行事件,第二种事件停止触发后依然会再执行一次事件
双剑合璧
那我们想要一个什么样的呢?
有人就说了:我想要一个有头有尾的!就是鼠标移入能立刻执行,停止触发的时候还能再执行一次!
所以我们综合两者的优势,然后双剑合璧,写一版代码:
function throttle(func, wait) {
var timeout, context, args, result;
var previous = 0;
var later = function() {
previous = +new Date();
timeout = null;
func.apply(context, args)
};
var throttled = function() {
var now = +new Date();
//下次触发 func 剩余的时间
var remaining = wait - (now - previous);
context = this;
args = arguments;
// 如果没有剩余的时间了或者你改了系统时间
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
};
return throttled;
}
效果演示如下:
我们可以看到:鼠标移入,事件立刻执行,晃了 3s,事件再一次执行,当数字变成 3 的时候,也就是 6s 后,我们立刻移出鼠标,停止触发事件,9s 的时候,依然会再执行一次事件。
优化
但是我有时也希望无头有尾,或者有头无尾,这个咋办?
那我们设置个 options 作为第三个参数,然后根据传的值判断到底哪种效果,我们约定:
leading:false 表示禁用第一次执行
trailing: false 表示禁用停止触发的回调
我们来改一下代码:
function throttle(func, wait, options) {
var timeout, context, args, result;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : new Date().getTime();
timeout = null;
func.apply(context, args);
if (!timeout) context = args = null;
};
var throttled = function() {
var now = new Date().getTime();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
};
return throttled;
}
取消
在 debounce 的实现中,我们加了一个 cancel 方法,throttle 我们也加个 cancel 方法:
...
throttled.cancel = function() {
clearTimeout(timeout);
previous = 0;
timeout = null;
}
...
注意
我们要注意 underscore 的实现中有这样一个问题:
那就是 leading:false 和 trailing: false 不能同时设置。
如果同时设置的话,比如当你将鼠标移出的时候,因为 trailing 设置为 false,停止触发的时候不会设置定时器,所以只要再过了设置的时间,再移入的话,就会立刻执行,就违反了 leading: false,bug 就出来了,所以,这个 throttle 只有三种用法:
container.onmousemove = throttle(getUserAction, 1000);
container.onmousemove = throttle(getUserAction, 1000, {
leading: false
});
container.onmousemove = throttle(getUserAction, 1000, {
trailing: false
});
至此我们已经完整实现了一个 underscore 中的 throttle 函数,恭喜,撒花!
参考文章:https://github.com/mqyqingfeng/Blog/issues/26
跟着 underscore 学节流的更多相关文章
- 跟着underscore学防抖
前言 在前端开发中会遇到一些频繁的事件触发,比如: window 的 resize.scroll mousedown.mousemove keyup.keydown -- 为此,我们举个示例代码来了解 ...
- 【跟着大佬学JavaScript】之节流
前言 js的典型的场景 监听页面的scroll事件 拖拽事件 监听鼠标的 mousemove 事件 ... 这些事件会频繁触发会影响性能,如果使用节流,降低频次,保留了用户体验,又提升了执行速度,节省 ...
- 【跟着大佬学JavaScript】之lodash防抖节流合并
前言 前面已经对防抖和节流有了介绍,这篇主要看lodash是如何将防抖和节流合并成一个函数的. 初衷是深入lodash,学习它内部的好代码并应用,同时也加深节流防抖的理解.这里会先从防抖开始一步步往后 ...
- 【跟着stackoverflow学Pandas】 - Adding new column to existing DataFrame in Python pandas - Pandas 添加列
最近做一个系列博客,跟着stackoverflow学Pandas. 以 pandas作为关键词,在stackoverflow中进行搜索,随后安照 votes 数目进行排序: https://stack ...
- 【跟着stackoverflow学Pandas】 -Get list from pandas DataFrame column headers - Pandas 获取列名
最近做一个系列博客,跟着stackoverflow学Pandas. 以 pandas作为关键词,在stackoverflow中进行搜索,随后安照 votes 数目进行排序: https://stack ...
- 【跟着stackoverflow学Pandas】add one row in a pandas.DataFrame -DataFrame添加行
最近做一个系列博客,跟着stackoverflow学Pandas. 以 pandas作为关键词,在stackoverflow中进行搜索,随后安照 votes 数目进行排序: https://stack ...
- 【跟着stackoverflow学Pandas】Select rows from a DataFrame based on values in a column -pandas 筛选
最近做一个系列博客,跟着stackoverflow学Pandas. 以 pandas作为关键词,在stackoverflow中进行搜索,随后安照 votes 数目进行排序: https://stack ...
- 【跟着stackoverflow学Pandas】How to iterate over rows in a DataFrame in Pandas-DataFrame按行迭代
最近做一个系列博客,跟着stackoverflow学Pandas. 以 pandas作为关键词,在stackoverflow中进行搜索,随后安照 votes 数目进行排序: https://stack ...
- 【跟着stackoverflow学Pandas】“Large data” work flows using pandas-pandas大数据处理流程
最近做一个系列博客,跟着stackoverflow学Pandas. 以 pandas作为关键词,在stackoverflow中进行搜索,随后安照 votes 数目进行排序: https://stack ...
随机推荐
- Day 5-3 多态与多态性
多态与多态性 鸭子类型 多态与多态性 多态:一类事物有多种形态.比如,动物有多种形态,人,狗,猪,豹子.水也有多种形态,冰,雪,水蒸气. #多态:同一类事物的多种形态 import abc class ...
- Java 线程的生命周期
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态,在线程的生命周期中,它要经过新建(New).就绪(Runnable).运行(Running).阻塞(Blocked)和死 ...
- python爬虫之git的使用(coding.net的使用)
1.注册coding.net账号,然后登陆. 2.创建项目 套路和github都一样. 1.1.我们在远程仓库上创建了一个仓库,这样的话,我们需要在本地随便建立一个文件普通文件夹,进去以后,执行git ...
- python(Django之Logging、API认证)
一.Loging模块 用于方便的记录日志的模块 import logging logging.basicConfig(filename='log.log', format='%(asctime)s - ...
- 老男孩python学习自修【第三天】列表用法
列表的使用: list.append(value) 向列表增加元素 list.insert(index, value) 向列表指定元素插入元素 list.extend(newlist) 用新的列表扩展 ...
- 无法启动此程序,因为计算机丢失MSVCR110.dll
解决方法下: 1.首先是打开浏览器,在浏览器的地址栏里输入 http://www.microsoft.com/zh-CN/download/details.aspx?id=30679 这个网址来进行相 ...
- PHPStorm从入门到精通
1. 使用phpstorm+xdebug进行调试 首先,安装php的xdebug扩展 查看phpinfo中php的版本,php的安装位数,php的是否线程安全:根据这些下载对应的xdebug.dll ...
- AMS工作原理—— App启动概要
说明: 1. 通过Launcher或者startActivity启动最终的流程都是和上面的一致的. 2. AMP是AMS在App端(client端)的代理, ATP是ApplicationThread ...
- Spring 使用介绍(三)—— 资源
一.Resource接口 Spring提供Resource接口,代表底层外部资源,提供对底层外部资源的一致性访问接口 public interface InputStreamSource { Inpu ...
- java8的版本对组合式异步编程
讨论了Java 8中的函数式数据处理,它可以将对集合数据的多个操作以流水线的方式组合在一起.本节继续讨论Java 8的新功能,主要是一个新的类CompletableFuture,它是对65节到83节介 ...