一. 节流和防抖的含义
throttle 和 debounce 是解决请求和响应速度不匹配问题的两个方案。二者的差异在于选择不同的策略。
假设间隔都是15s;
throttle : 节流,即强制减少触发次数,规定特定时间内只能执行一次;
第一次请求进来马上执行。从第二次请求开始,如果下一次与上一次执行的间隔不到15s,则不执行;否则就执行。
假设频繁触发的话,最终表现为两次请求间隔就是15s左右;
如果间隔不够,抛弃的是后面的请求。保证每 X 毫秒恒定的执行次数,比如每200ms检查下滚动位置,并触发 CSS 动画。
debounce : 防抖,即停止触发后才执行
每次请求进来,都要等待15s,确定15s后没有新请求进来再执行。
假设频繁触发的话,最终表现为两次请求间隔是无限长
二. 使用场景
throttle : 页面滚动检测距离底部距离,然后加载数据
只需要保证特定时间内只触发一次就行了
debounce : input搜索框输入 调整页面大小resize
重点: 我们只关心最后一次的输入
三. 简单实现
debounce :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function debounce(func, wait) { var timeout;
return function () { var context = this; var args = arguments;
clearTimeout(timeout) timeout = setTimeout(function(){ func.apply(context, args) }, wait); } }
var count = 1; var container = document.getElementById('container');
function getUserAction() { container.innerHTML = count++; }; container.onmousemove = debounce(getUserAction, 1000);
|
throttle :
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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; } } }
|
四. underscore中的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
_.throttle = function(func, wait, options) { var context, args, result; var timeout = null; var previous = 0; if (!options) options = {}; var later = function() { previous = options.leading === false ? 0 : _.now(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function() { var now = _.now(); if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0 || remaining > wait) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
_.debounce = function(func, wait, immediate) { var timeout, args, context, timestamp, result;
var later = function() { var last = _.now() - timestamp;
if (last < wait && last > 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } };
return function() { context = this; args = arguments; timestamp = _.now(); var callNow = immediate && !timeout; if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; }
return result; }; };
|