首頁 > web前端 > js教程 > JavaScript效能最佳化之函數節流(throttle)與函數去抖(debounce)

JavaScript效能最佳化之函數節流(throttle)與函數去抖(debounce)

高洛峰
發布: 2016-12-29 10:08:36
原創
1508 人瀏覽過

函數節流,簡單地講,就是讓一個函數無法在很短的時間間隔內連續調用,只有當上一次函數執行後過了你規定的時間間隔,才能進行下一次該函數的調用。

函數節流的原理挺簡單的,估計大家都想到了,那就是定時器。當我觸發一個時間時,先setTimout讓這個事件延遲一會再執行,如果在這個時間間隔內又觸發了事件,那我們就clear掉原來的定時器,再setTimeout一個新的定時器延遲一會執行,就這樣。

以下場景往往因事件頻繁被觸發,因而頻繁執行DOM操作、資源載入等重行為,導致UI停頓甚至瀏覽器崩潰。

1. window物件的resize、scroll事件

2. 拖曳時的mousemove事件

3. 射擊遊戲中的mousedown、keydown事件

3. 射擊遊戲中的mousedown、keydown事件

其實對於window的resize事件,實際需求大多為停止改變大小n毫秒後執行後續處理;而其他事件大多的需求是以一定的頻率執行後續處理。針對這兩種需求就出現了debounce和throttle兩種解決方法。

throttle 和 debounce 是解決請求和回應速度不符問題的兩個方案。二者的差異在於選擇不同的策略。

throttle 等時間 間隔執行函數。

debounce 時間間隔 t 內若再次觸發事件,則重新計時,直到停止時間大於或等於 t 才執行函數。

一、throttle函數的簡單實作

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); 
} 
};}
登入後複製

呼叫方法

$(&#39;body&#39;).on(&#39;mousemove&#39;, throttle(function (event) 
{
console.log(&#39;tick&#39;);
}, 1000));
登入後複製

二、debounce函數的簡單實作

function debounce(fn, delay) 
{ 
var timer = null; 
return function () 
{ 
var context = this,
args = arguments; 
clearTimeout(timer); 
timer = setTimeout(function () { 
fn.apply(context, args); 
}, delay); 
};}
登入後複製

呼叫方法

$(&#39;input.username&#39;).keypress(debounce(function (event)
{
// do the Ajax request
}, 250));
登入後複製

reee會重複觸發的一些事件,如:mousemove,keydown,keyup,keypress,scroll等。

如果只綁定原生事件,不加以控制,會使得瀏覽器卡頓,使用者體驗差。為了提高js效能,建議在使用以上及類似事件的時候用函數節流或函數去抖加以控制。



四、underscore v1.7.0相關的源碼剖析


1. _.throttle函數

/** * 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 );
}
登入後複製

由上可見,underscore考慮了比較多的情況:options.leading:

由上可見,underscore考慮了比較多的情況:options.leading:🜎預設為true,表示第一次會執行,傳入{leading:false}則停用第一次執行options.trailing:最後一次是否執行,預設為true,表示最後一次會執行,傳入{trailing: false}表示最後一次不執行所謂第一次是否執行,是剛開始觸發事件時,要不要先觸發事件,如果要,則previous=0,remaining 為負值,則立即調用了函數所謂最後一次是否執行,是事件結束後,最後一次觸發了此方法,如果要執行,則設定定時器,即事件結束以後還要在執行一次。 remianing > wait 表示客戶端時間已修改過。


2. _.debounce函數 

_.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; 
};};
登入後複製

_.debounce實現的精彩之處我認為是透過遞歸啟動計時器來代替透過呼叫clearTimeout來調整呼叫func函數的延時執行。

以上所述是小編給大家介紹的JavaScript性能優化之函數節流(throttle)與函數去抖(debounce),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回覆大家的。在此也非常感謝大家對PHP中文網的支持!

更多JavaScript性能優化之函數節流(throttle)與函數去抖(debounce)相關文章請關注PHP中文網!

來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板