javascript防抖(Debouncing)和节流阀(Throttling)
Posted 刘翾
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javascript防抖(Debouncing)和节流阀(Throttling)相关的知识,希望对你有一定的参考价值。
中文原文链接: https://jinlong.github.io/2016/04/24/Debouncing-and-Throttling-Explained-Through-Examples/
英文原文链接: https://css-tricks.com/debouncing-throttling-explained-examples/
1. 序言
防抖(Debounce)和节流(throttle)都是用来控制某个函数在一定时间内执行多少次的技巧,两者相似而又不同。
当我们给 DOM 绑定事件的时候,加了防抖和节流的函数变得特别有用。为什么呢?因为我们在事件和函数执行之间加了一个控制层。记住,我们是无法控制 DOM 事件触发频率的。
看下滚动事件的例子:
// html
<h1>Number of scroll events </h1>
<a href="#" class="reset">Reset</a>
<div id="counter">0</div>
// css
body
background: #444444;
color: white;
font: 15px/1.51 system, -apple-system, ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif;
margin:0 auto;
max-width:600px;
padding:20px;
min-height:1000vh; /* 100 times viewport height */
#counter
position:fixed;
top:100px;
left:40%;
font-size:50px;
.reset
color:white;
text-decoration:none;
border:1px solid white;
padding:10px 20px;
background:rgba(0,0,0,0.1);
// js
var i = 0;
var $counter = $('#counter');
$(document).ready(function()
$(document).on('scroll', function()
$counter.html(i);
i++;
);
);
$('.reset').on('click', function()
$counter.html('');
i = 0;
)
演示图:
gif抓取帧率太低,可以自己本地写下代码试试
当使用触控板,滚动滚轮,或者拖拽滚动条的时候,一秒可以轻松触发30次事件。经我的测试,在智能手机上,慢慢滚动一下,一秒可以触发事件100次之多。这么高的执行频率,你的滚动回调函数压力大吗?
有个大佬建议的解决方案是,在onScroll
事件外部,每 250ms 循环执行一次。简单的技巧,避免了影响用户体验。
现如今,有一些稍微高端的方式处理事件。我来结合用例介绍下 Debounce,Throttle 和 requestAnimationFrame 吧。
2. 防抖动(Debounce)
防抖技术可以把多个顺序地调用合并成一次。 下面看个例子
<a class="trigger-area">Trigger area</a>
<a class="reset">Reset</a>
<div class="visualizations">
<h2>Raw events over time</h2>
<div id="raw-events" class="events"></div>
<h2>Debounced events
<span class="details"> 400ms, trailing</span></h2>
<div id="debounced-events" class="events"></div>
</div>
body
background: #444444;
color: white;
font: 15px/1.51 system, -apple-system, ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif;
margin:0 auto;
max-width:700px;
padding:20px;
.events
padding:0px 20px 10px 20px;
height: 23px;
.events span
height:17px;
width:6px;
display:inline-block;
border-right:1px solid #111;
.events span:last-of-type
border:2px solid black;
border-bottom: 4px solid #AAA;
border-top: 0px;
margin-bottom:-17px;
margin-left:-2px;
h2
margin:10px 0 5px 0;
clear:both;
font-weight: normal;
font-size:14px;
padding:6px 20px;
.trigger-area
margin: 0;
display:inline-block;
width: 200px;
height:50px;
border: 1px solid #5ed1ff;
padding: 28px 0 0 0;
text-align: center;
background-color: transparent;
cursor:pointer;
font-size:17px;
-webkit-user-select: none; /* Chrome / Safari */
-moz-user-select: none; /* Firefox all */
-ms-user-select: none; /* IE 10+ */
user-select: none; /* Likely future */
.trigger-area.active
background:#2F5065;
.clickme:hover,
.clickme:active
background-color: #333;
.clickme:active
padding: 4px 5px;
.reset
display:inline-block;
width: 120px;
padding: 10px 0 0 0;
text-align: center;
font-size:14px;
cursor:pointer;
color:#eee;
.visualizations
margin-top:10px;
background:rgba(0,0,0,0.2);
.details
font-size:13px;
color:#999;
/* stating the obvious: color0 represents our empty color */
.color0 transparent
.color1 background-color: #FFE589
.color2 background-color: #B9C6FF
.color3 background-color: #99FF7E
.color4 background-color: #FFB38A
.color5 background-color: #A5FCFF
.color6 background-color: #FF8E9B
.color7 background-color: #E3FF7E
.color8 background-color: #FFA3D8
.color9 background-color: #5ca6ff
.color10 background-color: #9BFFBB
$(document).ready(function()
var $rawDiv = $('#raw-events'),
$debounceDiv = $('#debounced-events'),
$triggerArea = $('.trigger-area'),
initialized = false,
frequency = 100,
barLength = 0,
globalColor = 2,
colorNeedChange = false,
interval_id,
rawColor = 0,
debounceColor = 0,
maxBarLength = 87;
var drawDebouncedEvent = _.debounce(function(div)
debounceColor = globalColor;
, frequency*4, leading:false, trailing:true);
var changeDebouncedColor = _.debounce(function(div)
// Change colors, to visualize easier the "group of events" that is reperesenting this debounced event
globalColor++;
if (globalColor > 9)
globalColor = 2;
, frequency*4, leading:false, trailing:true);
function draw_tick_marks()
// every x seconds, draw a tick mark in the bar
interval_id = setInterval(function()
barLength++;
$rawDiv.append('<span class="color' + rawColor + '" >');
$debounceDiv.append('<span class="color' + debounceColor + '" >');
rawColor = 0; // make it transparent again
debounceColor = 0; // make it transparent again
if (barLength > maxBarLength)
clearInterval(interval_id);
, frequency);
;
// Track Mouse movement or clicks for mobile
$triggerArea.on('click mousemove', function ()
if (!initialized)
initialized = true;
draw_tick_marks();
$(this).addClass('active');
rawColor = globalColor;
drawDebouncedEvent();
changeDebouncedColor();
);
$('.reset').on('click', function()
initialized = false;
$triggerArea.removeClass('active');
$rawDiv.empty();
$debounceDiv.empty();
barLength = 0;
clearInterval(interval_id);
);
);
效果图:
你可以看到连续快速的事件是如何被一个 debounce 事件替代的。但是如果事件触发的时间间隔过长,debounce 则不会生效。
2.1. 前缘(或者“immediate”)
你会发现,直到事件停止快速执行以后,debounce 事件才会触发相应功能。为何不立即触发呢?那样的话就跟原本的非 debounce 处理无异了。
直到两次快速调用之间的停顿结束,事件才会再次触发。
前缘 debounce 的例子,如下方代码:
<a class="trigger-area">Trigger area</a>
<a class="reset">Reset</a>
<div class="visualizations">
<h2>Raw events over time</h2>
<div id="raw-events" class="events"></div>
<h2>Debounced events
<span class="details"> 400ms, trailing</span></h2>
<div id="debounced-events" class="events"></div>
</div>
body
background: #444444;
color: white;
font: 15px/1.51 system, -apple-system, ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif;
margin:0 auto;
max-width:700px;
padding:20px;
.events
padding:0px 20px 10px 20px;
height: 23px;
.events span
height:17px;
width:6px;
display:inline-block;
border-right:1px solid #111;
.events span:last-of-type
border:2px solid black;
border-bottom: 4px solid #AAA;
border-top: 0px;
margin-bottom:-17px;
margin-left:-2px;
h2
margin:10px 0 5px 0;
clear:both;
font-weight: normal;
font-size:14px;
padding:6px 20px;
.trigger-area
margin: 0;
display:inline-block;
width: 200px;
height:50px;
border: 1px solid #5ed1ff;
padding: 28px 0 0 0;
text-align: center;
background-color: transparent;
cursor:pointer;
font-size:17px;
-webkit-user-select: none; /* Chrome / Safari */
-moz-user-select: none; /* Firefox all */
-ms-user-select: none; /* IE 10+ */
user-select: none; /* Likely future */
.trigger-area.active
background:#2F5065;
.clickme:hover,
.clickme:active
background-color: #333;
.clickme:active
padding: 4px 5px;
.reset
display:inline-block;
width: 120px;
padding: 10px 0 0 0;
text-align: center;
font-size:14px;
cursor:pointer;
color:#eee;
.visualizations
margin-top:10px;
background:rgba(0,0,0,0.2);
.details
font-size:13px;
color:#999;
/* stating the obvious: color0 represents our empty color */
.color0 transparent
.color1 background-color: #FFE589
.color2 background-color: #B9C6FF
.color3 background-color: #99FF7E
.color4 background-color: #FFB38A
.color5 background-color: #A5FCFF
.color6 background-color: #FF8E9B
.color7 background-color: #E3FF7E
.color8 background-color: #FFA3D8
.color9 background-color: #5ca6ff
.color10 background-color: #9BFFBB
$(document).ready(function()
var $rawDiv = $('#raw-events'),
$debounceDiv = $('#debounced-events'),
$triggerArea = $('.trigger-area'),
initialized = false,
frequency = 100,
barLength = 0,
globalColor = 2,
colorNeedChange = false,
interval_id,
rawColor = 0,
debounceColor = 0,
maxBarLength = 87;
var drawDebouncedEvent = _.debounce(function(div)
debounceColor = globalColor;
, frequency*4, leading:true, trailing:false);
// 在 underscore.js 中,选项叫 immediate ,而不是 leading:
var changeDebouncedColor = _.debounce(function(div)
// Change colors, to visualize easier the "group of events" that is reperesenting this debounced event
globalColor++;
if (globalColor > 9)
globalColor = 2;
, frequency*4, leading:false, trailing:true);
function draw_tick_marks()
// every x seconds, draw a tick mark in the bar
interval_id = setInterval(function()
barLength++;
$rawDiv.append('<span class="color' + rawColor + '" >');
$debounceDiv.append('<span class="color' + debounceColor + '" >');
rawColor = 0; // make it transparent again
debounceColor = 0; // make it transparent again
if (barLength > maxBarLength)
clearInterval(interval_id);
, frequency);
;
// Track Mouse movement or clicks for mobile
$triggerArea.on('click mousemove', function ()
if (!initialized)
initialized = true;
draw_tick_marks();
$(this).addClass('active');
rawColor = globalColor;
drawDebouncedEvent();
changeDebouncedColor();
);
$('.reset')无敌秘籍之 — JavaScript手写代码