降频

某些类型的事件可能会连续、迅速触发多次(例如mousemovescroll事件)。处理这类事件时,你必须小心谨慎,防止处理任务耗时过长,否则处理器会占据过多事件,导致用户与文档交互变得非常慢。

若你需要在这类处理器中编写一些重要任务,可以使用setTimeout来确保不会频繁进行这些任务。我们通常称之为“事件降频(Debounce)”。有许多方法可以完成该任务。

在第一个示例中,当用户输入某些字符时,我们想要有所反应,但我们不想在每个按键事件中立即处理该任务。当用户输入过快时,我们希望暂停一下然后进行处理。我们不是立即在事件处理器中执行动作,而是设置一个定时器。我们也会清除上一次的定时器(如果有),因此当两个事件触发间隔过短(比定时器延时短),就会取消上一次事件设置的定时器。

  1. <textarea>Type something here...</textarea>
  2. <script>
  3. let textarea = document.querySelector("textarea");
  4. let timeout;
  5. textarea.addEventListener("input", () => {
  6. clearTimeout(timeout);
  7. timeout = setTimeout(() => console.log("Typed!"), 500);
  8. });
  9. </script>

undefined传递给clearTimeout或在一个已结束的定时器上调用clearTimeout是没有效果的。因此,我们不需要关心何时调用该方法,只需要每个事件中都这样做即可。

如果我们想要保证每次响应之间至少间隔一段时间,但不希望每次事件发生时都重置定时器,而是在一连串事件连续发生时能够定时触发响应,那么我们可以使用一个略有区别的方法来解决问题。例如,我们想要响应"mousemove"事件来显示当前鼠标坐标,但频率只有 250ms。

  1. <script>
  2. let scheduled = null;
  3. window.addEventListener("mousemove", event => {
  4. if (!scheduled) {
  5. setTimeout(() => {
  6. document.body.textContent =
  7. `Mouse at ${scheduled.pageX}, ${scheduled.pageY}`;
  8. scheduled = null;
  9. }, 250);
  10. }
  11. scheduled = event;
  12. });
  13. </script>