Madogiwa Blog

主に技術系の学習メモに使っていきます。

JavaScript: setTimeoutを使ってイベント発生時に実行する処理を抑制するMEMO

スクロールやテキストエリアの入力等、比較的大量になりやすいイベントをJavaScriptを使ってハンドリングして、さらにサーバーサイドにリクエストを投げたりしているとクライアントサイドだけでなくバックエンドの負荷が高まるのでイベント発火の頻度を抑制したいと思うこともあるかと思います。

そんなときにsetTimeoutを使うとイベントの発火頻度を抑制出来ると知ったので、そのへんのやり方をメモしておきます📝

やりかた

今回はscrollイベントを検知するような以下のコードで考えていこうと思います。

document.addEventListener('scroll', () => { console.log('scroll!!') })

現状だと非常に大量のイベントが発火してしまいます🔥

ではsetTimeoutを使ってイベント発火を抑制してみます。

まずはイベント発火の抑制の仕組みを持つ以下のような処理を用意します。

let timer = null
export const suppression = (callback, interval) => {
  if (timer) clearTimeout(timer);
  timer = window.setTimeout(callback, interval);
}

流れを説明するとsetTimeoutを管理するtimerという変数を持ちます。 suppressionという名前で関数を公開し、その中ではtimerがあればtimerで管理しているsetTimeoutをクリアして、なければsetTimeoutでcallbackで渡された処理を引数で渡されたinteval分遅延して実行しています。

つまりcallbackで渡された処理が遅延されている場合はsetTimeoutがクリアされるので実行がキャンセルされ、最後に発生したsetTimeoutのみが実行されるわけですね👩‍🚒

以下が実際に使ってみたサンプルです。scrollイベントはスクロール中断続的に発火するものの、addEventListenerで指定したcallbackは最後に発生したscrollイベントでしか実行されません。

import { suppression } from './suppression.js'
const INTERVAL_MILLISECOND = 200
document.addEventListener('scroll', () => { 
  suppression(() => { console.log('scroll!!') }, INTERVAL_MILLISECOND) 
})

おわりに

イベント抑制の仕組みを使うと、ハンドリングして外部のサービスを利用しているような処理を実行しているようなケースがあった場合に、

なんかしらの理由から悪意のあるユーザーにハンドリングしているイベントが知られたときにconsoleから大量実行されて外部サービスのRate Limitに引っかかってサービス全体が利用出来なくなるようなことにも多少は対策になるような気もするので、イベントに依存した処理の実行時には注意したいですね・・・!

また、setTimeout以外にもLodashにはthrottleというメソッドがあったりといろいろな方法があるみたいです👀

参考

stackoverflow.com

qiita.com