lodashのdebounceとthrottleの違い


参考


WEBページ上のボタンが連打された際にアクセス流量制限をするために、lodashのdebounceやthrottleを使うことがあります。
debounceもthrottleもどちらもリクエスト間の流量制限ができるのですが、具体的に何が変わるのか調べました。

先にまとめ

debounceとthrottleの違いは以下の通りです。
オプションは指定せずデフォルトで呼び出す場合を想定しています。

debounce

処理呼び出し後、指定時間経過後に発火します。
指定時間内に処理が連続して呼び出された場合は最後に呼び出されてから指定時間後に発火します。
debounceメソッドの処理はsetTimeoutで実行されるので、awaitを指定しても待ちません。

throttle

処理呼び出し後、すぐに処理が走ります。
指定時間内に処理が連続して呼び出された場合は、指定時間間隔で発火します。
throttleメソッドの処理は最初に実行される1回はawaitで待ちますが、指定時間後に最初に即走る処理は待ちますが、throttleで遅延させている処理は待ちません。

おすすめ

debounce(throttle)はオプションを指定することで挙動をカスタマイズできるので、必要なら自分の期待する挙動となるようにオプションを指定するのがオススメ。

調べた内容

debounceについて

debounceについてはこちらに記載があります。
https://lodash.com/docs/4.17.15#debounce

_.debounce(func, [wait=0], [options={}])

各項目については以下の通り。

項目 内容
func 流量制限対象の処理(function)です。
wait 処理が呼び出されてから実行するまでの待機時間です。待機中に再度呼び出されたら、そこからさらに待機時間待ちます。
option 任意オプションをオブジェクトで指定します。
option.leading 待機前に処理を実行するかどうか。デフォルト:false。
option.maxWait 処理が連続して呼び出されたとしても、maxWaitで指定した時間が経過したら処理を走らせます。デフォルト:0(指定なし)。
option.trailing 待機後に処理を実行するかどうか。デフォルト:true。

つまり、optionをデフォルトのまま呼び出すのであれば、debounceは呼び出されてから指定時間待機してから発火します。
maxWaitの指定が無いので待機時間中に呼び出され続けたら、ずっと処理は発火しません。

throttleについて

throttleについてはこちらに記載があります。
https://lodash.com/docs/4.17.15#throttle

_.throttle(func, [wait=0], [options={}])

各項目については以下の通り。

項目 内容
func 流量制限対象の処理(function)です。
wait 処理が呼び出される間隔の時間です。
option 任意オプションをオブジェクトで指定します。
option.leading 待機前に処理を実行するかどうか。デフォルト:true。
option.trailing 待機後に処理を実行するかどうか。デフォルト:true。

optionはdebounceとほぼ同じですが、leadingのデフォルトがtrueでmaxWaitの指定がありません。

throttleのソースを見ると分かりますが、throttleはdebounceのオプションを指定して呼び出しているだけです。
https://github.com/lodash/lodash/blob/4.17.15/lodash.js#L10897-L10913

デフォルトでleadingがtrueになり、waitで指定された時間がoption.maxWaitにも指定されています。
つまり、throttleはデフォルト値が違うdebounceです。

図解

ボタンクリックのイベントハンドラにdebounce/throttleをデフォルトオプションで設定したとします。

1回クリック

1回だけクリックされた時の挙動は以下の通りです。

debounceの場合はwait後、throttleの場合はwait前に処理が走ります。
debounceだとクリックしてから反応するまで、1秒未満かもしれませんが少しタイムラグが感じられるかもしれません。

連打クリック

一瞬でクリック連打された場合は以下の通りです。

debounceについては1回クリックした際と同じ挙動になりますが、throttleについてはwait後にも処理が走ります。
throttleだと2回処理が走ってしまうのは微妙ですね。

クリックし続ける

wait中に一定間隔でクリックし続けた場合は以下の通りです。

debounceはクリックし続けている間は処理は走らず、最後のクリックからwaitで指定した時間だけ待って処理が走ります。
throttleはクリックし続けている間もwaitで指定した時間間隔で処理が走ります。
あまり何度も走らせたくない処理であればdebounceで、一定間隔空けば何度走っても良いのであればthrottleが向いてそうです。

思ったこと

debounceもthrottleも期待する挙動とはズレている事もあるので、
期待する挙動となるようにdebounceのオプションを設定すると良いのかなと思いました。

例えばクリックした時に遅延なく処理が走って欲しくて、かつ一瞬で連打した際も一定間隔でクリックし続けた場合も1回だけ処理が走って欲しい場合、
optionのleadingがtrueでtrailingがfalseになれば良さそうです。

_.debounce(callback, wait, {leading: true, trailing: false})

もし一定間隔でクリックし続けた場合は定期的に処理が走って欲しいのであれば、debounceではなくthrottleを使えば良いです。

_.throttle(callback, wait, {leading: true, trailing: false})
// debounce(callback, wait, {leading: true, maxWait: wait, trailing: false}) と同じ

などなど、optionの指定次第で可能性は広がると思いました。