HTML5テクニカルノート
Lodash: _.debounce()と_.throttle()でコールバックの呼び出しを間引く
- ID: FN2103001
- Technique: ECMAScript 2015
- Library: Lodash 4.17.21
コールバックが立て続けに呼び出されるとき、実行を間引く関数がLodashの_.debounce()
と_.throttle()
です。_.debounce()
は呼び出しがひと息ついたら、つまり前回の実行から一定間隔空いたときに、直近の呼び出しを処理します。あらかじめ定めた間隔の間に、それぞれ1度までしか実行しないのが_.throttle()
です。
01 _.debounce() ー コールバックの呼び出しが一定時間空いたら実行する
_.debounce()
は、待ち時間が過ぎたら呼び出されるデバウンス関数(コールバック)をつくって返します。待ち時間は、コールバックが前回呼び出されたときから計測され、単位はミリ病です。デバウンス関数には、コールバックのつぎの呼び出しを止めるcancel()
と、直ちに実行するflush()
メソッドが備わります。さらに、オプションとして決められるのは、待ち時間に対してコールバックを先呼び出し(leading
)するか、後呼び出し(trailing
)するかです(デフォルトはtrailing
がtrue
)。
leading
とtrailing
のオプションは、それぞれに論理値が与えられます。ともにtrue
にした場合でも、待ち時間の間に複数回呼ばれたのでなければ、終了時にコールバックが重複して実行されることはありません。待ち時間のwait
を0にして、leading
はfalse
に定めると、コールバックの呼び出しはあくまで終了時になります(setTimeout()
で待ち時間を0
にしたときと同様)。
_.debounce(func, [wait=0], [options={}])
引数
func (Function)
: デバウンス関数(コールバック)。[wait=0] (number)
: 待ち時間のミリ秒。[options={}] (Object)
: オプションを収めるオブジェクト。[options.leading=false] (boolean)
: 待ち時間に対してコールバックを先呼び出しするかどうか。[options.maxWait] (number)
: コールバックの呼び出しを待つ最大時間。[options.trailing=true] (boolean)
: 待ち時間に対してコールバックを後呼び出しするかどうか。
戻り値
(Function)
: 新たなデバウンス関数が返される。
例
つぎのコードは、mousemove
イベントのコールバック呼び出しを_.debounce()
で間引く例です。マウスを動かすのを止めてから3秒(3000ミリ秒)経つと、最後のコールバックが実行されて、コンソールにポインタのxy座標が示されます。デフォルトは後呼び出し(オプション引数のtrailing
がtrue
)ですので、マウスを動かし続けているかぎり、コールバックは実行されません。
document.addEventListener('mousemove', _.debounce( (event) => console.log('position:', { x: event.clientX, y: event.clientY }) , 3000 ));
そこで、leading
をtrue
、trailing
はfalse
にして、先呼び出しに変えましょう。すると、マウスを動かした途端にコールバックが実行されます。そのあと、マウスの動きを止めると、いつまで経ってもコールバックは呼び出されません。これが先呼び出しの結果です。マウスを動かしさえすれば、その瞬間にコールバックは実行されます。
const Square = ({ id }) => {document.addEventListener('mousemove', _.debounce( (event) => console.log('position:', { x: event.clientX, y: event.clientY }) , 3000, { leading: true, trailing: false} ));
さらに、maxWait
のオプションを加えると、マウスを動かしてコールバックの呼び出しが続いていても、その時間内にはつねに1回だけ実行されます。
document.addEventListener('mousemove', _.debounce( (event) => console.log('position:', { x: event.clientX, y: event.clientY }) , 3000, { leading: true, trailing: false, maxWait: 1000 } ));
02 throttle() ー コールバックの実行を一定時間あたり1回に間引く
_.throttle()
は、待ち時間ごとに1度だけ呼び出されるスロットル関数(コールバック)をつくって返します。待ち時間は、コールバックが前回呼び出されたときから計測され、単位はミリ病です。スロットル関数には、コールバックのつぎの呼び出しを止めるcancel()
と、直ちに実行するflush()
メソッドが備わります。さらに、オプションとして決められるのは、待ち時間に対してコールバックを先呼び出し(leading
)するか、後呼び出し(trailing
)するかです(デフォルトはtrailing
がtrue
)。
leading
とtrailing
のオプションは、それぞれに論理値が与えられます。ともにtrue
にした場合でも、待ち時間の間に複数回呼ばれたのでなければ、終了時にコールバックが重複して実行されることはありません。待ち時間のwait
を0にして、leading
はfalse
に定めると、コールバックの呼び出しはあくまで終了時になります(setTimeout()
で待ち時間を0
にしたときと同様)。
_.throttle(func, [wait=0], [options={}])
引数
func (Function)
: スロットル関数(コールバック)。[wait=0] (number)
: 待ち時間のミリ秒。[options={}] (Object)
: オプションを収めるオブジェクト。[options.leading=false] (boolean)
: 待ち時間に対してコールバックを先呼び出しするかどうか。[options.trailing=true] (boolean)
: 待ち時間に対してコールバックを後呼び出しするかどうか。
戻り値
(Function)
: 新たなスロットル関数が返される。
例
つぎのコードは、mousemove
イベントのコールバック呼び出しを_.throttle()
で間引く例です。マウスをはじめて動かすと、コールバックが直ちに呼び出されて、コンソールにポインタのxy座標が示されます。さらに動かし続けても、3秒(3000ミリ秒)に1回しかコールバックは実行されません。デフォルトでは、オプションの先呼び出し(leading
がtrue
)と後呼び出し(trailing
がtrue
)ともに設定されています。そのため、先呼び出しのあと複数のコールバック呼び出しがあれば、待ち時間終了直前の関数が後呼び出しされるのです。
document.addEventListener('mousemove', _.throttle( (event) => console.log('position:', { x: event.clientX, y: event.clientY }) , 3000 ));
03 待ち時間の空け方
ふたつの関数の待ち時間の空け方について補っておきましょう。
まず、はじめのコールバック実行から、待ち時間の一定間隔が決まります。_.throttle()
であればそれらひとつひとつの間隔の中に複数のコールバックは実行されません。つぎに、コールバックの実行の間隔も、決められた一定時間が保たれます。_.debounce()
の場合、一定間隔の中で呼び出しがなかっただけでなく、最後の呼び出し(実行されていなくても)から決められた時間が過ぎなければ、コールバックは実行されないのです。
- はじめのコールバック実行から、待ち時間の一定間隔が決まる。
- コールバックの実行の間隔も、決められた一定時間が保たれる。
ふたつの関数について、それぞれオプションふたつの違いを文章で説明するのは難しいでしょう。「Throttle & Debounce behavior (lodash)」が例(Example)を図示して、具体的にどう間引かれるか解説しています。前述のふたつの原則を頭において見比べてみてください。また、「Debouncing and Throttling Explained Through Examples」には、具体的なCodePenのコードが複数紹介されているので参考になるでしょう。
作成者: 野中文雄
作成日: 2021年03月10日
Copyright © 2001-2020 Fumio Nonaka. All rights reserved.