JavaScript を使っているなら lodash は大変便利なのですが、関数が多すぎてどれ使ったらいいのか解りづらいのが難点です。
ここではその中でも連続する呼び出しを無視してくれる debounce
と throttle
という負荷対策用の関数を紹介します。また、この2つの似た関数がどう違うのかも説明します。
連続する呼び出しを無視したい、とはどういうことか
たとえば、mouse move イベントはマウスの座標が動くたびにすごい頻度で発生します。これに応じて画面を同期させたり、サーバに問い合わせしてしまうと大変なので一般的には0.2秒とかマウスが止まったら同期や問い合わせが走るように実装するかと思います。
別の例でいえば、textbox のインクリメンタルサーチ実装で、文字打つたびにサーバ問い合わせしたら大変なので、入力がある程度止まった時点で問い合わせするのが一般的です。
この処理をすごく簡単に実装できるのが debounce です。
debounce の使い方
$panel.on('mousemove', function () {
// 画面同期 or サーバ問い合わせ (ただし高頻度で呼ばれる)
});
これを下記のように _.debounce(関数, wait時間)
で囲むだけで実現できます。
$panel.on('mousemove', _.debounce(function () {
// 画面同期 or サーバ問い合わせ (ただし 0.2 秒動きが止まったら呼ばれる)
}, 200));
また、下記のように maxWait
を指定することで、マウスが動いてても一定秒に 1 回は最低限呼ばれるようにもできます。
$panel.on('mousemove', _.debounce(function () {
// 画面同期 or サーバ問い合わせ
}, 200, { maxWait: 1000 }));
debounce
の注意点
_.debounce()
は自身では実行せず、関数を返すということに注意してください。上記のようにコールバックをラップするのにはちょうど良いですが、通常処理の中に setTimeout()
的なノリで記述すると実行されなくて悩むことになるかも。
(もし発火させたいなら、_.debounce(~)()
のように末尾に ()
を付ければよいでしょう。)
似た関数 throttle があるけど?
throttle は debounce と似ていますが、使い道が若干異なります。throttle
は、一定時間に1度しか実行されないようにするためのものです。
たとえば、データに変更があるたびに定期的にサーバへと同期させたいとします。とはいえ、スペック的に1秒に1回以上は同期したくないという場合に throttle
の出番となります。
もしこれを、debounce
でやってしまうと、更新がある限り永遠にサーバに同期されません。maxWait
を指定した場合はほぼ同じ動きにはなりますが、throttle
では最初に1度実行されるという点が大きな違いとなります。
throttle
は、まず1回実行し、その後一定期間、呼び出しを監視し、呼び出しがあれば最後の呼び出しだけを実行する(呼び出しがなければその後は何もしない)というものです。最初に1回実行するので後続呼び出しが無い場合も効率的です。
0s 呼び出し1 呼び出し1 を実行する
↓
呼び出し2(実行されない)
↓
呼び出し3(実行されない)
↓
呼び出し4
↓
1s 呼び出し4 を実行する
debounce
が一定時間止まったら実行させる、という考え方なのに対して、throttle
は一定時間に1回というペースを守らせるという考え方です。
throttle の使い方
function onChangeData (newData) { // データが変更されるたびに呼ばれる
// サーバへの同期処理
}
これを下記のように _.throttle(関数, wait時間)
で囲むだけで実現できます。
var _changeData = _.throttle(function (newData) {
// サーバへの同期処理
}, 1000);
function onChangeData (newData) { // データが変更されるたびに呼ばれる
_changeData();
}