随着前端技术的不断发展,现代前端应用越来越依赖于异步交互,这也带来了多种问题:比如用户的快速点击,搜索自动补全等等,这些交互往往会导致频繁地发送网络请求,甚至崩溃应用。为了解决这些问题,有时我们需要一些额外的手段来在异步请求中进行控制,这也就是本文所要介绍的 debounce 和 throttle。
debounce 的应用场景
Debounce(防抖动)是一种在限制某些事件被重复调用的情况下,能够限制其被调用次数的技术。这种技术的应用场景通常是在某些需要用户交互的场景下,比如按钮点击、输入框搜索等等。
按钮点击
当用户在界面上疯狂点击按钮的时候,我们需要在他停止操作的时候才进行处理。这种情况实现 debounce 的代码如下所示:
var button = document.querySelector('#button'); Rx.Observable.fromEvent(button, 'click') .debounceTime(250) .subscribe(event => { console.log('Clicked!'); });
当用户点击按钮时,时间流会发出一连串的值,但是 debounceTime 会阻止这些值直接流到 subscribe 回调中,并通过阻塞这些值的方式确保时间流处于空闲状态。在暂停时间内,如果用户再次点击按钮,debounceTime 会重置暂停时间,以让用户有更多时间来点击。
输入框搜索
当用户在输入框中输入关键字时,每次输入可能会触发查询操作,这也会导致不必要的流量和服务器负载。为了解决这个问题,我们可以使用 debounce 控制查询频率:
-- -------------------- ---- ------- --- --------- - ---------------------------------- ---------------------------------- -------- ------------------- ------ -- --------------- ------------- -- ------------ -- -- ---------------- -- ------------------------ ------------------ -- - --------------------- ---展开代码
上面例子中,debounceTime 会定时获取输入框的内容,如果用户在 1 秒内输入了新的内容,debounceTime 将重启等待计时器,而不是将输入字符发送到后台 API 进行搜索。
throttle 的应用场景
Throttle(节流)是一种防止某些事件在指定时间内被频繁调用的技术。这种技术的应用场景通常是在某些需要限制事件发生的情况下,比如滚动事件、鼠标移动事件等等。
滚动事件
在滚动事件中,通常使用 throttle 监听事件,并处理事件发生的频率。例如,如果您正在开发一个网站,你想要在用户滚动页面时将图片淡入,所以你可能会使用下面的代码来创建 throttle 的滚动事件监听器:
Rx.Observable.fromEvent(document, 'scroll') .throttleTime(50) .subscribe(event => { console.log('Scrolled!'); });
当用户进行滚动时,时间流不会直接流向 subscribe 回调中,而是通过 throttleTime 来阻塞事件,确保在每个 50ms 内只有一个事件能够通过处理。
鼠标移动事件
在鼠标移动事件中,throttle 可以帮助我们限制事件的频率,以调整性能和响应速度。例如,当我们开发一个游戏时,我们可能希望控制每秒钟鼠标移动的次数,而不是每次都处理它。
Rx.Observable.fromEvent(document, 'mousemove') .throttleTime(100) .subscribe(event => { console.log(`Mouse at (${event.clientX}, ${event.clientY})`); });
在上面的例子中,throttleTime 每隔 100 毫秒发出一次鼠标移动事件,并记录鼠标指针的运动数据。这样做可以避免浏览器被过度压力产生的缓慢和不安,同时保证游戏中角色的响应速度。
优化方式
无论是 debounce 还是 throttle,它们都是为了解决事件事件的代码问题而设计的,而且它们都有一些可以进一步优化的地方。
map 高阶函数中的 debounce 和 throttle
Rx.Observable.fromEvent(document, 'mousemove') .map(() => Math.random()) .throttle(() => Rx.Observable.interval(1500)) .map(() => 1) .debounce(() => Rx.Observable.interval(1900)) .subscribe(value => { console.log(value); });
注意,以上三个地方都出现了高阶函数,这些函数有时会产生多个事件,而 debounceTime 和 throttleTime 是等待固定时间,而 RxJS 自带的 debounce 和 throttle 可以以函数回调的方式支援这种情况。在上面的代码示例中我们可以使用高阶 debounce 和 throttle 来管理准确的时间和事件流。
控制最小时间
Rx.Observable.fromEvent(document, 'mousemove') .throttleTime(50) .subscribe(event => { console.log(`Mouse at (${event.clientX}, ${event.clientY})`); });
在上面例子中,我们使用了 50 毫秒的时间来限制每次鼠标移动的重复处理,这个时间可以是一个可供推断的数值或是一个实时回调函数,这个定时器使得 50 毫秒内的所有鼠标移动事件都能够在第一个准确地被处理,因此,一旦时间流到 subscribe 回调中,它们也能被处理。
过滤首次事件
在某些场景下,你可能会希望在持续监听事件之前先过滤掉事件的第一次发生。下面是一个 debounce 和 throttle 结合使用且过滤首次事件的示例:
Rx.Observable.fromEvent(document, 'mousemove') .skip(1) .debounceTime(500) .throttleTime(1000) .subscribe(event => { console.log(`Mouse at (${event.clientX}, ${event.clientY})`); });
在上面的代码示例中,我们在之前的事件方法上添加了一个会过滤掉第一次事件的 skip 操作符。在 debounce 和 throttle 后面的时间周期也有所改变,这是我们应该根据特定情况进行自定义的。
总结
在本文中,我们学习了 debounce 和 throttle 的用法以及如何进行进一步的优化、自定义,通过上述应用场景我们可以看出,这两个函数有许多不同的使用方式,而且可以有效地改善应用程序的性能。在未来的开发中,我们应该尝试使用 debounce 和 throttle 来优化常见问题,并研究如何制定适合自己工程的最佳实践。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/64ae0ad248841e98949ff868