性能提速:debounce(防抖)、throttle(节流/限频)
debounce与throttle是用户交互处理中常用到的性能提速方案,debounce用来实现防抖动,throttle用来实现节流(限频)。那么这两个方法到底是什么(what)?为何要用(why-解决什么问题)?具体的实现原理,以及函数运行过程是怎样的呢(how)?
1、what?
连续操作:两个操作之间的时间间隔小于设定的阀值,这样子的一连串操作视为连续操作。
debounce(防抖):一个连续操作中的处理,只触发一次,从而实现防抖动。
throttle:一个连续操作中的处理,按照阀值时间间隔进行触发,从而实现节流。
图1 debounce、throttle运行图
如图所示,其中delay=4,由于红色操作序列与绿色操作序列之间的时间间隔小于delay,所以这两个序列被视为一个连续操作行为。
- debounceTail:执行操作在连续操作完成之后,触发;
- debounceStart:执行操作在连续操作完成之前,触发;
- throttle:在一个连续操作行为中,每间隔delay的时间触发1次。
结合运行图,可以更好的理解debounce、throttle的作用。
2、why?
常用情景:
- a、scroll事件:当页面发生滚动时,scroll事件会被频繁的触发,1s触发可高达上百次。在scroll事件中,如果有复杂的操作(特别是影响布局的操作),将会大大影响性能,甚至导致浏览器崩溃。所以,对其进行防抖、限频很重要。
- b、click事件:用户进行click事件时,有可能连续触发点击(用户本意并非双击)。该操作有可能是不小心多次连续点击,也可能是页面状况不好的情况下,期待尽快得到反馈的有意行为;但这样的操作,反而会加剧性能问题,因此也有必要考虑防抖、限频。
- c、input事件:如sug等需要通过ajax及时获得数据的情况,需要进行限频,防止频繁的请求发生,减少服务器压力的同时,提高页面响应性能。
- d、touchmove事件:同scroll事件类似。
还有许多其他业务场景会出现频繁操作的情况,不一一列举。debounce可用于:防止用户的多次click提交;scroll下拉刷新时,同一位置多次请求数据等。throttle可应用于,scroll设置定位等的频繁位置计算;拖拽的频繁位置计算等。
3、how?
怎样实现?其代码实现如下:
// 防抖 且首次执行
// 采用原理:第一操作触发,连续操作时,最后一次操作打开任务开关(并非执行任务),任务将在下一次操作时触发)
function debounceStart(fn, delay, ctx) {
let immediate = true
let movement = null
return function() {
let args = arguments
// 开关打开时,执行任务
if (immediate) {
fn.apply(ctx, args)
immediate = false
}
// 清空上一次操作
clearTimeout(movement)
// 任务开关打开
movement = setTimeout(function() {
immediate = true
}, delay)
}
}
// 防抖 尾部执行
// 采用原理:连续操作时,上次设置的setTimeout被clear掉
function debounceTail(fn, delay, ctx) {
let movement = null
return function() {
let args = arguments
// 清空上一次操作
clearTimeout(movement)
// delay时间之后,任务执行
movement = setTimeout(function() {
fn.apply(ctx, args)
}, delay)
}
}
// 限频,每delay的时间执行一次
function throttle(fn, delay, ctx) {
let isAvail = true
return function() {
let args = arguments
// 开关打开时,执行任务
if (isAvail) {
fn.apply(ctx, args)
isAvail = false
// delay时间之后,任务开关打开
setTimeout(function() {
isAvail = true
}, delay)
}
}
}
// 调用
btn.onclick = debounceStart(function(event) {
console.log('100ms')
}, 100, this)
window.onscroll = throttle(function(event) {
console.log('100ms')
}, 100, this)
如上代码,使用了闭包,将isAvail等父级变量存储在了内存当中,实现状态切换。同时,通过apply将任务函数的上下文ctx(在类、对象内操作时,其作用更明显);参数arguments(如调用中的event),存入最终的任务执行函数当中。通过timer的clear和set来控制任务的触发,同时需留意任务执行与任务开关打开的区别。任务执行是timer到达,就将触发任务;任务开关打开是timer到达时,只将状态变更,需要用户的再一次操作,才能实施真正的任务触发。
通过控制台可以看到,不进行限频时,scroll在1s内可以触发高达上100次,增加了限频之后,就将scroll的触发控制在一定的范围内。
4、思考
图2 throttle运行标示图
在实际的使用场景当中,我们会发现,用户最后一次操作并没有后续的处理,也就是最后一次操作的状态将丢失。在某些应用场景当中,可能造成状态处理不准确。如通过scroll事件判断是否到达页面底部,如果到达,则提示用户。使用throttle方法进行节流,在到达底部之前,小于delay的时间间隔内,触发了一次位置判断操作;下一次触发将在delay时间之后,但在那之前,scroll事件已经结束了,所以无法获取最后scroll到底部的位置,也就不会触发提示。
如何优化呢?
可以结合debounceTail的功能,其可以实现最后一次操作的捕捉,如图所示:
图3,throttle加强运行图
其代码如下:
// 限频,每delay的时间执行一次
function throttle(fn, delay, ctx) {
let isAvail = true
let count = false
let movement = null
return function() {
count = true
let args = arguments
if (isAvail) {
fn.apply(ctx, args)
isAvail = false
count = false
setTimeout(function() {
isAvail = true
}, delay)
}
if (count) {
clearTimeout(movement)
movement = setTimeout(function() {
fn.apply(ctx, args)
}, 2 * delay)
}
}
}
增加movement来记录和清除最终操作状态;用count来避免与限频的重合;如此便实现了捕获最终操作状态的限频操作。
tips:其中大量使用setTimeout()的操作,在高级浏览器中,可以使用requestAnimationFrame来替代setTimeout操作,从而提高性能。requestAnimationFrame的原理、优势及低版本的兼容,可以查阅张鑫旭的博客,写得很详细:CSS3动画那么强,requestAnimationFrame还有毛线用?
性能提速:debounce(防抖)、throttle(节流/限频)的更多相关文章
- “浅入浅出”函数防抖(debounce)与节流(throttle)
函数防抖与节流是日常开发中经常用到的技巧,也是前端面试中的常客,但是发现自己工作一年多了,要么直接复用已有的代码或工具,要么抄袭<JS高级程序设计>书中所述"函数节流" ...
- 详解防抖函数(debounce)和节流函数(throttle)
本文转自:https://www.jianshu.com/p/f9f6b637fd6c 闭包的典型应用就是函数防抖和节流,本文详细介绍函数防抖和节流的应用场景和实现. 函数防抖(debounce) 函 ...
- throttle(节流)和debounce(防抖)
防抖和节流都是用来控制频繁调用的问题,但是这两种的应用场景是有区别的. throttle(节流) 有一个调用周期,在一个很长的时间里分为多段,每一段执行一次.例如onscroll,resize,500 ...
- 防抖(debounce)和节流(throttle)
场景说明:一般我们在前端页面中会给元素绑定click.scroll.onmousemove.resize等事件,这些事件的执行函数如果是去发请求获取数据的话,我们无意识的连续点击或者连续滚动会给服务器 ...
- throttle(节流函数) 与 debounce(防抖动函数)理解与实现
我们会对一些触发频率较高的事件进行监听,(如:resize scroll keyup事件) 如果在回调里执行高性能消耗的操作(如反复操作dom, 发起ajax请求等),反复触发时会使得性能消耗提高,浏 ...
- 防抖(debounce)和 节流(throttling)
防抖(debounce)和 节流(throttling) 1.防抖和节流出现的原因 防抖和节流是针对响应跟不上触发频率这类问题的两种解决方案. 在给DOM绑定事件时,有些事件我们是无法控制触发频率的. ...
- debounce还是throttle(去抖和节流)
debounce 去抖 我的理解很简单,比方说window.onscroll会疯狂触发handler,此时给它一个debounce(handler, delayTime). 就是不管你延时时间内触发了 ...
- JS的防抖和节流
数个月之前,在一次前端的性能优化中,接触到了JS中防抖和节流,一开始还不明白他们的应用在哪里,可后来才知道,这是前端中最基础的性能优化,在绑定 scroll .resize 这类事件时,当它发生时,它 ...
- 详谈js防抖和节流
本文由小芭乐发表 0. 引入 首先举一个例子: 模拟在输入框输入后做ajax查询请求,没有加入防抖和节流的效果,这里附上完整可执行代码: <!DOCTYPE html> <html ...
随机推荐
- js-随机图片
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" ...
- VBA 代码
Private Declare PtrSafe Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA& ...
- 应该知道的Linux技巧【转】
这篇文章来源于Quroa的一个问答<What are some time-saving tips that every Linux user should know?>—— Linux用户 ...
- Mac下替代Total Commander的工具推荐
[推荐]:Nimble Commander 轻量小巧,免费版与收费版区别不大,比较稳定,支持sftp等其他网络存储,支持自定义热键,预览等. http://magnumbytes.com/ [其他]: ...
- ASP.NET MVC5 高级编程-学习日记-第二章 控制器
2.1 控制器的角色 MVC模式中的控制器(Controller)主要负责响应用户的输入,冰球在响应时修改模型(Model).通过这种方式,MVC模式中的控制器主要关注的是应用程序流.输入数据的处理, ...
- 【BZOJ3551】 [ONTAK2010]Peaks加强版
BZOJ3551 [ONTAK2010]Peaks加强版 Solution Kruscal重构树后发现可以对于小于的离散化然后倍增+主席树找到上一个的可行解. 然后就可以了. 如果数组开的不好,容易在 ...
- 常见配置redis.conf介绍
参数说明redis.conf 配置项说明如下:1. Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程 daemonize no2. 当Redis以守护进程方式运行 ...
- 10_python_函数进阶
一.函数参数-动态参数 形参:位置参数.默认值参数.动态参数 动态参数分为两种:动态接收位置参数 *args .动态接收关键字参数 *kwargs 1. *args def chi(*foo ...
- Spring Boot中使用Redis数据库
引入依赖 Spring Boot提供的数据访问框架Spring Data Redis基于Jedis.可以通过引入spring-boot-starter-redis来配置依赖关系. <depend ...
- Spring 全局异常处理
[参考文章]:Spring全局异常处理的三种方式 [参考文章]:Spring Boot 系列(八)@ControllerAdvice 拦截异常并统一处理 [参考文章]:@ControllerAdvic ...