/*
callback:callback functionname
interval: millisecond */ function debounce(callback, interval) {
let debounceTimeoutId; return function(...args) {
clearTimeout(debounceTimeoutId);
debounceTimeoutId = setTimeout(() => callback.apply(this, args), interval);
};
} function throttle(callback, interval) {
let enableCall = true; return function(...args) {
if (!enableCall) return; enableCall = false;
callback.apply(this, args);
setTimeout(() => enableCall = true, interval);
}
}
/*
* 频率控制 返回函数连续调用时,fn 执行频率限定为每多少时间执行一次
* @param fn {function} 需要调用的函数
* @param delay {number} 延迟时间,单位毫秒
* @param immediate {bool} 给 immediate参数传递false 绑定的函数先执行,而不是delay后后执行。
* @param debounce {bool} 给 immediate参数传递false 绑定的函数先执行,而不是delay后后执行。debounce
* @return {function}实际调用函数
*/ var throttle = function(fn, delay, immediate, debounce) {
var curr = +new Date(),//当前时间
last_call = 0,
last_exec = 0,
timer = null,
diff, //时间差
context,//上下文
args,
exec = function () {
last_exec = curr;
fn.apply(context, args);
};
return function () {
curr = +new Date();
context = this,
args = arguments,
diff = curr - (debounce ? last_call : last_exec) - delay;
clearTimeout(timer);
if (debounce) {
if (immediate) {
timer = setTimeout(exec, delay);
} else if (diff >= 0) {
exec();
}
} else {
if (diff >= 0) {
exec();
} else if (immediate) {
timer = setTimeout(exec, -diff);
}
}
last_call = curr;
}
};
var setval =  function(){
var mydate = new Date();
var s = mydate.getSeconds();
var ms = mydate.getMilliseconds();
document.getElementById("ipt").value=s+":"+ms; } //以下两种绑定方法都可以,特别是jquery绑定时.注意 $("#btn")[0]
//document.getElementById("btn").onclick = throttle(function(){ setval(); },1000,true,false); 
$("#btn")[0].onclick = throttle(function(){ setval(); },1000,true,false);

以下转自:https://programmingwithmosh.com/javascript/javascript-throttle-and-debounce-patterns/

 June 17th, 2019

Comments

JavaScript patterns: Throttle and Debounce

Do you need to handle a specific event, but you want to control how many times your handler will be called in a given period of time? This is a very common problem and often happens when listening to events such as scrollmousemove or resize, which are triggered dozens of times a second. But sometimes it’s desirable even for less “frenetic” events, like, for example, to limit an action that occurs when a certain button is clicked. In this article, we’ll cover two patterns to control the repeated call of event handlers: throttle and debounce.

The example app

In order to understand both patterns, we will use a simple example application that shows the current mouse coordinates in the screen and how many times these coordinates were updated. By looking at this counter, you will find it easier to compare how these two techniques can control the calling of an event handler. This is our app without any event control technique applied:

render();

document.body.addEventListener('mousemove', logMousePosition);

let callsCount = 0;
const positionsEl = document.querySelector('.app__positions');
function logMousePosition(e) {
callsCount++;
positionsEl.innerHTML = `
X: ${e.clientX},
Y: ${e.clientY},
calls: ${callsCount}
`;
} function render() {
document.querySelector('#app').innerHTML = `
<h1 class="app__title">Mouse position (Without event control) </h1>
<div class="app__positions"></div>
`;
}

And here is how this initial version of our app behaves:

Full example.

Did you notice the calls count? In just 7 seconds, the logMousePosition function was called 320 times! Sometimes you don’t want a given event handler to be called so many times in a period of time so short. Now, let’s see how to solve this issue.

Throttle

The throttle pattern limits the maximum number of times a given event handler can be called over time. It lets the handler be called periodically, at specified intervals, ignoring every call that occurs before this wait period is over. This technique is commonly used to control scrolling, resizing and mouse-related events.

We need to adapt the example app to use the throttle pattern. In order to do so, let’s change the addEventListener call of the original code to include the throttling logic:

let enableCall = true;
document.body.addEventListener('mousemove', e => {
if (!enableCall) return; enableCall = false;
logMousePosition(e);
setTimeout(() => enableCall = true, 300);
});

In the above code:

  1. We’re declaring a enableCall variable to control if the throttling interval is already over. This flag is initially set to true, in order to allow the handler to be called the first time.
  2. Every time a mousemove event is triggered, we test if the enableCall variable is true. If not, that means that the minimum wait period is not over yet and we won’t call the event handler that time.
  3. However, if the enableCall variable is true, we can execute the event handler normally. However, before doing so, we need to set our control flag to false in order to prevent the handler to be called again during the wait interval.
  4. After calling the event handler, we use the setTimeout function to set our flag back to true after 300 milliseconds. This way, our handler will only be authorized to run again when this minimum interval is over.

Full example.

Now, let’s see how the app behaves after applying this technique:

The throttling logic works perfectly! Now, there’s a minimum interval of 300 milliseconds between the updates, drastically reducing our calls count.

A reusable throttle function

In order to make this code reusable, let’s extract it to a separate function:

document.body.addEventListener('mousemove', throttle(logMousePosition, 300));

function throttle(callback, interval) {
let enableCall = true; return function(...args) {
if (!enableCall) return; enableCall = false;
callback.apply(this, args);
setTimeout(() => enableCall = true, interval);
}
}

The logic here is the same as above. When called, the throttle function returns a new event listener. In this version, we’re using a common function in order to preserve the original this context and apply it to the passed callback.

Full example.

Debounce

The debounce pattern allows you to control events being triggered successively and, if the interval between two sequential occurrences is less than a certain amount of time (e.g. one second), it completely ignores the first one. This process is repeated until it finds a pause greater than or equal to the desired minimum interval. Once it happens, only the last occurrence of the event before the pause will be considered, ignoring all the previous ones. A common use case for this pattern is to limit requests in a search box component that suggests topics while the user is typing.

Let’s adapt our example app to use the debounce pattern:

let debounceTimeoutId;
document.body.addEventListener('mousemove', (e) => {
clearTimeout(debounceTimeoutId);
debounceTimeoutId = setTimeout(() => logMousePosition(e), 300);
});

The above piece of code contains the essence of the debounce pattern. Every time the event we want to control is fired, we schedule a call to the handler for 300 milliseconds later (by using setTimeout) and cancel the previous scheduling (with clearTimeout). This way, we’re constantly delaying the execution of the handler function until the interval between two sequential events is equal to or greater than a given threshold time.

To sum up:
Let’s say we have a given event being triggered two times (A and B) and there’s an X interval between these two occurrences. If we want to control this event by applying a debounce of Y milliseconds, here’s how it will work:

  • If X < Y, the A call will be ignored.
  • If X >= Y, the B call is considered.
  • In both cases, if the new interval after B is greater than or equal to Y, B will be considered too.

Now, let’s see how it behaves in practice:

Full example

A reusable debounce function

Finally, we can extract the pattern logic to a reusable function. This way, we can easily apply it in similar situations in the future:

document.body.addEventListener('mousemove', debounce(logMousePosition, 300));
function debounce(callback, interval) {
let debounceTimeoutId; return function(...args) {
clearTimeout(debounceTimeoutId);
debounceTimeoutId = setTimeout(() => callback.apply(this, args), interval);
};
}

Full example

Conclusion

  • The debounce pattern delays the calling of the event handler until a pause happens. This technique is commonly used in search boxes with a suggest drop-down list. By applying this pattern, we can prevent unnecessary requests to the backend while the user is typing.
  • The throttle pattern is more suitable for events that are triggered many times in a short period of time. This technique is normally used to control scrolling, resizing and mouse-related events. By using throttle, we can filter repeated executions of an event handler, enforcing a minimum wait time between calls.

If you really want to master the fundamentals of JavaScript, you need to check out Mosh’s JavaScript course. It’s the best JavaScript course out there! And if you liked this article, share it with others as well!

最新文章

  1. codeigniter钩子的使用
  2. 二 Java利用等待/通知机制实现一个线程池
  3. js搜索框输入提示(高效-ys8)
  4. JavaScript中创建类,赋值给ajax中的data参数
  5. CANopen笔记1
  6. SqlServer参数化脚本与自动参数化(简单参数化)
  7. Gradle学习
  8. nginx + tomcat集群和动静资源分离
  9. elisp debug
  10. delphi数字签名验证及能够获取数字签名文件信息(利用wintrust.dll的导出函数,翻译一下)
  11. 深入浅出Node.js (1) - Node简介
  12. (c#)SKYPE API项目总结(一)
  13. js中bind、call、apply函数的用法 (转载)
  14. 学习札记 ----wind7下如何安装SqlServer数据库
  15. svn up出现类似svn: Error converting entry in directory &#39;.&#39; to UTF-8问题解决
  16. unity UI如何开启(显示)或者关闭(隐藏)Panel界面最好?
  17. 将html前端代码提取公因数(5)
  18. android测试--常用控件测试及测试经验(常见)
  19. SpringBoot从入门到逆天(1)
  20. 关于try catch

热门文章

  1. unity, Animation crossfade需要两动画在时间上确实有交叠
  2. 在没有spineRunTime情况下手动使用spine数据
  3. asp.net中Cookie的用法【转】
  4. memcache 启动参数
  5. WebAPI Post类型传参报错“找不到与该请求匹配的操作”
  6. Win7系统上配置使用Intellij Idea 13的SVN插件
  7. [shiro] Wildcard string cannot be null or empty. Make sure permission strings are properly formatted.
  8. 用Opera Mobile调试手机版网页【转】
  9. URL地址中的转义符
  10. ros的相关link