防抖和节流
在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。
防抖函数(debounce)
1 const debounce = (cb, delay = 1000) => { 2 let timer = null; 3 return function (...args) { 4 const context = this; 5 if (timer) clearTimeout(timer); 6 timer = setTimeout(() => { 7 cb.apply(context, args); 8 timer = null; 9 }, delay); 10 } 11 }
若延迟delay设置为1000(默认值)则cb(回调函数)只会在停止触发1s后执行如果一直不断地触发则回调函数始终不执行。
使用
下面是一个简单的使用示例后续介绍的防抖和节流函数的使用方式也是相似的。
1 const callback = () => { 2 console.log(Math.random()); 3 } 4 const handle = debounce(callback, 1000); 5 window.addEventListener('scroll', handle);
防抖函数(第一次触发会立即执行)
1 const debounceImmediate = (cb, delay = 1000, immediate = true) => { 2 let timer = null; 3 return function (...args) { 4 const context = this; 5 const execNow = immediate && !timer; 6 if (timer) clearTimeout(timer); 7 timer = setTimeout(() => { 8 cb.apply(context, args); 9 timer = null; 10 }, delay); 11 execNow && cb.apply(this, args); 12 } 13 }
当设置immediate=true(默认值)、delay=1000(默认值)时第一次触发会立即执行回调函数。后续执行和普通的防抖函数一样只有在停止触发1s后回调函数才会执行如果还是一直不断地触发则回调函数始终不执行。
适用场景:
- 按钮提交场景:防止多次提交按钮,只执行最后提交的一次
- 服务端验证场景:表单验证需要服务端配合,只执行一段连续的输入事件的最后一次,还有搜索联想词类似功能
节流throttle
概念
规定在一个单位时间内,只能触发一次函数。如果这个单位事件内触发多次函数,只有一次生效。
节流函数(使用时间戳)
1 const throttleUseTimeStamp = (cb, delay = 1000) => { 2 let startTime = Date.now(); 3 return function(...args) { 4 const context = this; 5 const now = Date.now(); 6 if (now - startTime >= delay) { 7 cb.apply(context, args); 8 startTime = Date.now(); 9 } 10 } 11 }
若delay=1000则在1s内只会执行一次回调函数。
节流函数的实现(使用定时器)
1 const throttleUseTimer = (cb, delay) => { 2 let timer = null; 3 return function(...args) { 4 const context = this; 5 if (!timer) { 6 timer = setTimeout(() => { 7 cb.apply(context, args); 8 timer = null; 9 }, delay); 10 } 11 } 12 }
若delay=1000则在1s内只会执行一次回调函数。
节流函数的实现(第一次触发立即执行最后一次触发也会执行)
1 const throttleExecMore = function(cb, delay) { 2 let timer = null; 3 let startTime = Date.now(); 4 return function(...args) { 5 const curTime = Date.now(); 6 const remaining = delay - (curTime - startTime); 7 const context = this; 8 timer && clearTimeout(timer); 9 if (remaining <= 0) { 10 // 第一次触发执行 11 cb.apply(context, args); 12 startTime = Date.now(); 13 } else { 14 // 最后一次触发也会执行 15 timer = setTimeout(() => { 16 cb.apply(context, args); 17 timer = null; 18 }, remaining); 19 } 20 } 21 }
适用场景
- 拖曳场景:固定时间只执行一次,防止超高频次触发位置变动
- 缩放场景:监控浏览器 resize
- 动画场景:避免短时间内多次触发动画引起性能问题