サイトトップ

Director Flash 書籍 業務内容 プロフィール

HTML5テクニカルノート

Lodash: _.debounce()と_.throttle()でコールバックの呼び出しを間引く


コールバックが立て続けに呼び出されるとき、実行を間引く関数がLodashの_.debounce()_.throttle()です。_.debounce()は呼び出しがひと息ついたら、つまり前回の実行から一定間隔空いたときに、直近の呼び出しを処理します。あらかじめ定めた間隔の間に、それぞれ1度までしか実行しないのが_.throttle()です。

01 _.debounce() ー コールバックの呼び出しが一定時間空いたら実行する

_.debounce()は、待ち時間が過ぎたら呼び出されるデバウンス関数(コールバック)をつくって返します。待ち時間は、コールバックが前回呼び出されたときから計測され、単位はミリ病です。デバウンス関数には、コールバックのつぎの呼び出しを止めるcancel()と、直ちに実行するflush()メソッドが備わります。さらに、オプションとして決められるのは、待ち時間に対してコールバックを先呼び出し(leading)するか、後呼び出し(trailing)するかです(デフォルトはtrailingtrue)。

leadingtrailingのオプションは、それぞれに論理値が与えられます。ともにtrueにした場合でも、待ち時間の間に複数回呼ばれたのでなければ、終了時にコールバックが重複して実行されることはありません。待ち時間のwaitを0にして、leadingfalseに定めると、コールバックの呼び出しはあくまで終了時になります(setTimeout()で待ち時間を0にしたときと同様)。


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

引数

戻り値

つぎのコードは、mousemoveイベントのコールバック呼び出しを_.debounce()で間引く例です。マウスを動かすのを止めてから3秒(3000ミリ秒)経つと、最後のコールバックが実行されて、コンソールにポインタのxy座標が示されます。デフォルトは後呼び出し(オプション引数のtrailingtrue)ですので、マウスを動かし続けているかぎり、コールバックは実行されません。


document.addEventListener('mousemove', _.debounce(
	(event) => console.log('position:', { x: event.clientX, y: event.clientY })
	, 3000
));

そこで、leadingtruetrailingfalseにして、先呼び出しに変えましょう。すると、マウスを動かした途端にコールバックが実行されます。そのあと、マウスの動きを止めると、いつまで経ってもコールバックは呼び出されません。これが先呼び出しの結果です。マウスを動かしさえすれば、その瞬間にコールバックは実行されます。


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)するかです(デフォルトはtrailingtrue)。

leadingtrailingのオプションは、それぞれに論理値が与えられます。ともにtrueにした場合でも、待ち時間の間に複数回呼ばれたのでなければ、終了時にコールバックが重複して実行されることはありません。待ち時間のwaitを0にして、leadingfalseに定めると、コールバックの呼び出しはあくまで終了時になります(setTimeout()で待ち時間を0にしたときと同様)。


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

引数

戻り値

つぎのコードは、mousemoveイベントのコールバック呼び出しを_.throttle()で間引く例です。マウスをはじめて動かすと、コールバックが直ちに呼び出されて、コンソールにポインタのxy座標が示されます。さらに動かし続けても、3秒(3000ミリ秒)に1回しかコールバックは実行されません。デフォルトでは、オプションの先呼び出し(leadingtrue)と後呼び出し(trailingtrue)ともに設定されています。そのため、先呼び出しのあと複数のコールバック呼び出しがあれば、待ち時間終了直前の関数が後呼び出しされるのです。


document.addEventListener('mousemove', _.throttle(
	(event) => console.log('position:', { x: event.clientX, y: event.clientY })
	, 3000
));

03 待ち時間の空け方

ふたつの関数の待ち時間の空け方について補っておきましょう。

まず、はじめのコールバック実行から、待ち時間の一定間隔が決まります。_.throttle()であればそれらひとつひとつの間隔の中に複数のコールバックは実行されません。つぎに、コールバックの実行の間隔も、決められた一定時間が保たれます。_.debounce()の場合、一定間隔の中で呼び出しがなかっただけでなく、最後の呼び出し(実行されていなくても)から決められた時間が過ぎなければ、コールバックは実行されないのです。

  1. はじめのコールバック実行から、待ち時間の一定間隔が決まる。
  2. コールバックの実行の間隔も、決められた一定時間が保たれる。

ふたつの関数について、それぞれオプションふたつの違いを文章で説明するのは難しいでしょう。「Throttle & Debounce behavior (lodash)」が例(Example)を図示して、具体的にどう間引かれるか解説しています。前述のふたつの原則を頭において見比べてみてください。また、「Debouncing and Throttling Explained Through Examples」には、具体的なCodePenのコードが複数紹介されているので参考になるでしょう。


作成者: 野中文雄
作成日: 2021年03月10日


Copyright © 2001-2020 Fumio Nonaka.  All rights reserved.