ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript パフォーマンスの最適化: 関数スロットル (スロットル) と関数デバウンス (デバウンス)
関数スロットルとは、簡単に言うと、関数を短い時間間隔内で連続して呼び出すことができないことを意味し、最後の関数実行から指定した時間が経過した場合にのみ、次の関数呼び出しを行うことができます。
機能スロットルの原理は非常に単純で、誰もが考えたことがあると思います。それはタイマーです。時間をトリガーするときは、まず setTimeout でイベントをしばらく遅らせてから、この時間間隔内にイベントが再度トリガーされた場合は、元のタイマーをクリアしてから、新しいタイマーを setTimeout してしばらく実行を遅らせます。 。 、それだけです。
以下のシナリオでは、イベントが頻繁にトリガーされることが多いため、DOM 操作、リソースの読み込み、その他の負荷の高いアクティビティが頻繁に実行され、その結果、UI が一時停止したり、ブラウザーがクラッシュしたりすることもあります。
1. ウィンドウオブジェクトのサイズ変更とスクロールイベント
2. ドラッグ時のマウス移動イベント
3. テキスト入力と自動のキーアップイベント
実際、ウィンドウのサイズ変更イベントの実際の要件は、後続の処理を実行する前に n ミリ秒後にサイズの変更を停止することですが、他のイベントのほとんどの要件は、後続の処理を特定の頻度で実行することです。これら 2 つのニーズに応えて、デバウンスとスロットルという 2 つのソリューションが登場しました。
スロットルとデバウンスは、リクエストとレスポンスの速度の不一致の問題を解決する 2 つのソリューションです。 2 つの違いは、異なる戦略の選択にあります。
throttle 関数を等時間間隔で実行します。
debounce 時間間隔 t 内にイベントが再びトリガーされた場合、時間は再開され、停止時間が t 以上になるまで関数は実行されません。
1. スロットル関数の簡単な実装
function throttle(fn, threshhold, scope) { threshhold || (threshhold = 250); var last, timer; return function () { var context = scope || this; var now = +new Date(), args = arguments; if (last && now - last + threshhold < 0) { // hold on to it clearTimeout(deferTimer); timer = setTimeout(function () { last = now; fn.apply(context, args); }, threshhold); } else { last = now; fn.apply(context, args); } };}メソッド呼び出し
$('body').on('mousemove', throttle(function (event) { console.log('tick'); }, 1000));2. デバウンス関数の簡単な実装
function debounce(fn, delay) { var timer = null; return function () { var context = this, args = arguments; clearTimeout(timer); timer = setTimeout(function () { fn.apply(context, args); }, delay); };}メソッド呼び出し
$('input.username').keypress(debounce(function (event) { // do the Ajax request }, 250));3. 簡単なカプセル化の実装
/** * throttle * @param fn, wait, debounce */var throttle = function ( fn, wait, debounce ) { var timer = null, // 定时器 t_last = null, // 上次设置的时间 context, // 上下文 args, // 参数 diff; // 时间差 return funciton () { var curr = + new Date(); var context = this, args = arguments; clearTimeout( timer ); if ( debounce ) { // 如果是debounce timer = setTimeout( function () { fn.apply( context, args ); }, wait ); } else { // 如果是throttle if ( !t_last ) t_last = curr; if ( curr - t_last >= wait ) { fn.apply( context, wait ); context = wait = null; } } }}/** * debounce * @param fn, wait */var debounce = function ( fn, wait ) { return throttle( fn, wait, true ); }繰り返しトリガーされるイベント (mousemove、keydown、keyup、keypress、scroll など)。
ネイティブ イベントを制御せずにバインドするだけの場合、ブラウザがフリーズし、ユーザー エクスペリエンスが低下します。 js のパフォーマンスを向上させるために、上記および同様のイベントを使用するときに、関数スロットルまたは関数デバウンスを使用して制御することをお勧めします。
4. アンダースコア v1.7.0 に関連するソースコード分析
1. _.throttle 関数
_.throttle = function(func, wait, options) { var context, args, result; var timeout = null; // 定时器 var previous = 0; // 上次触发的时间 if (!options) options = {}; var later = function() { previous = options.leading === false ? 0 : _.now(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function() { var now = _.now(); // 第一次是否执行 if (!previous && options.leading === false) previous = now; // 这里引入了一个remaining的概念:还剩多长时间执行事件 var remaining = wait - (now - previous); context = this; args = arguments; // remaining <= 0 考虑到事件停止后重新触发或者 // 正好相差wait的时候,这些情况下,会立即触发事件 // remaining > wait 没有考虑到相应场景 // 因为now-previous永远都是正值,且不为0,那么 // remaining就会一直比wait小,没有大于wait的情况 // 估计是保险起见吧,这种情况也是立即执行 if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout); timeout = null; } previous = now; result = func.apply(context, args); if (!timeout) context = args = null; // 是否跟踪 } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; };};上記からわかるように、アンダースコアはさまざまな状況を考慮します。初めて。デフォルトは true です。これは、初めて実行されることを意味します。{leading: false} が渡された場合、最初の実行は無効になります。デフォルトは true で、これは最後に実行されることを意味します。 {leading: false} が渡された場合、いわゆる初回実行かどうかがトリガーされるかどうかを示します。イベントが最初にトリガーされると、previous=0 になり、残りが負の値になると、この関数はイベント終了後に直ちに呼び出されます。このメソッドは最後にトリガーされます。実行したい場合は、タイマーを設定します。つまり、イベントの終了後に再度実行されます。 remianing > wait は、クライアント時間が変更されたことを示します。 2. _.debounce 関数
_.debounce = function(func, wait, immediate) { // immediate默认为false var timeout, args, context, timestamp, result; var later = function() { // 当wait指定的时间间隔期间多次调用_.debounce返回的函数,则会不断更新timestamp的值,导致last < wait && last >= 0一直为true,从而不断启动新的计时器延时执行func var last = _.now() - timestamp; if (last < wait && last >= 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } }; return function() { context = this; args = arguments; timestamp = _.now(); // 第一次调用该方法时,且immediate为true,则调用func函数 var callNow = immediate && !timeout; // 在wait指定的时间间隔内首次调用该方法,则启动计时器定时调用func函数 if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; } return result; };};
上記は、エディターが紹介した JavaScript パフォーマンス最適化のための関数 throttling (スロットル) と関数 debounce (デバウンス) です。ご質問がございましたら、メッセージを残してください。全員にすぐに返信します。また、PHP 中国語 Web サイトをサポートしていただきありがとうございます。
JavaScript パフォーマンス最適化関数スロットリング (throttle) および関数 debounce (デバウンス) に関連するその他の記事については、PHP 中国語 Web サイトに注目してください。