Home  >  Article  >  Web Front-end  >  JavaScript performance optimization: function throttling (throttle) and function debounce (debounce)

JavaScript performance optimization: function throttling (throttle) and function debounce (debounce)

高洛峰
高洛峰Original
2016-12-29 10:08:361455browse

Function throttling, simply put, means that a function cannot be called continuously within a short time interval. Only when the time interval you specify has passed after the last function execution, the next function call can be made.

The principle of function throttling is quite simple. I guess everyone has thought of it, that is the timer. When I trigger a time, I first setTimeout to delay the event for a while and then execute it. If the event is triggered again within this time interval, then we clear the original timer and then setTimeout a new timer to delay execution for a while. ,that's all.

The following scenarios are often caused by frequent triggering of events and frequent execution of DOM operations, resource loading and other heavy activities, resulting in UI pauses or even browser crashes.

1. The resize and scroll events of the window object

2. The mousemove event during dragging

3. In shooting games mousedown, keydown events

4. Text input, automatically completed keyup events

In fact, for the resize event of the window, the actual requirement is mostly to stop changing the size n milliseconds later Perform subsequent processing; while most other events require subsequent processing to be performed at a certain frequency. In response to these two needs, two solutions, debounce and throttle, have emerged.

throttle and debounce are two solutions to solve the problem of request and response speed mismatch. The difference between the two lies in the choice of different strategies.

throttle Execute the function at other time intervals.

debounce If the event is triggered again within the time interval t, the time will be restarted, and the function will not be executed until the stop time is greater than or equal to t.

1. Simple implementation of throttle function

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); 
} 
};}

Calling method

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

2. Simple implementation of debounce function

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

Calling method

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

3. Simple encapsulation implementation

/** * 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 );
}

Summary: These two methods are suitable for some events that will be triggered repeatedly, such as: mousemove, keydown, keyup, keypress, scroll, etc.
If you only bind native events without controlling them, the browser will freeze and the user experience will be poor. In order to improve js performance, it is recommended to use function throttling or function debounce to control when using the above and similar events.

4. Source code analysis related to underscore v1.7.0

1. _.throttle function

_.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; 
};};

As can be seen from the above, underscore has considered more Situation: options.leading:

Whether to execute for the first time, the default is true, which means it will be executed for the first time. Passing in {leading:false} disables the first execution of options.trailing: Whether to execute for the last time, the default is true, which means it will be executed for the last time. Passing in {trailing: false} means that it will not be executed for the last time. The so-called whether to execute for the first time means whether to trigger the event first when the event is first triggered. If so, Then previous=0, and remaining is a negative value, the function is called immediately. The so-called whether to execute for the last time means that this method is triggered for the last time after the event ends. If it is to be executed, set a timer, that is, it will be executed after the event ends. once. remianing > wait indicates that the client time has been modified.

2. _.debounce function

_.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; 
};};

I think the wonderful thing about _.debounce is that it starts the timer recursively instead of adjusting the delayed execution of the func function by calling clearTimeout.

The above is the function throttling (throttle) and function debounce (debounce) for JavaScript performance optimization introduced by the editor. I hope it will be helpful to you. If you have any questions, please leave me a message. , the editor will reply to everyone in time. I would also like to thank you all for your support of the PHP Chinese website!

For more articles related to JavaScript performance optimization function throttling (throttle) and function debounce (debounce), please pay attention to the PHP Chinese website!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn