Sta*_*tec 134 javascript debouncing
我对javascript中的"debouncing"函数很感兴趣,这里写的:http://davidwalsh.name/javascript-debounce-function
不幸的是,代码没有清楚地解释清楚,让我理解.任何人都可以帮我弄清楚它是如何工作的(我在下面留下了我的评论).总之,我真的不明白这是如何工作的
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
Run Code Online (Sandbox Code Playgroud)
编辑:复制的代码片段之前有callNow错误的位置.
Mal*_*alk 113
问题中的代码稍微改变了链接中的代码.在链接中,检查是否(immediate && !timeout)在创建新的timout之前.拥有它后立即模式永远不会开火.我已经更新了我的答案,以便从链接中注释工作版本.
function debounce(func, wait, immediate) {
// 'private' variable for instance
// The returned function will be able to reference this due to closure.
// Each call to the returned function will share this common timer.
var timeout;
// Calling debounce returns a new anonymous function
return function() {
// reference the context and args for the setTimeout function
var context = this,
args = arguments;
// Should the function be called now? If immediate is true
// and not already in a timeout then the answer is: Yes
var callNow = immediate && !timeout;
// This is the basic debounce behaviour where you can call this
// function several times, but it will only execute once
// [before or after imposing a delay].
// Each time the returned function is called, the timer starts over.
clearTimeout(timeout);
// Set the new timeout
timeout = setTimeout(function() {
// Inside the timeout function, clear the timeout variable
// which will let the next execution run when in 'immediate' mode
timeout = null;
// Check if the function already ran with the immediate flag
if (!immediate) {
// Call the original function with apply
// apply lets you define the 'this' object as well as the arguments
// (both captured before setTimeout)
func.apply(context, args);
}
}, wait);
// Immediate mode and no wait timer? Execute the function..
if (callNow) func.apply(context, args);
}
}
/////////////////////////////////
// DEMO:
function onMouseMove(e){
console.clear();
console.log(e.x, e.y);
}
// Define the debounced function
var debouncedMouseMove = debounce(onMouseMove, 50);
// Call the debounced function on every mouse move
window.addEventListener('mousemove', debouncedMouseMove);Run Code Online (Sandbox Code Playgroud)
Mat*_*ens 54
这里要注意的重要一点是debounce产生一个"关闭" 变量的函数timeout.timeout在生成函数的每次调用期间,即使在debounce返回之后,变量仍然可以访问,并且可以在不同的调用之间进行更改.
总体思路debounce如下:
第一点是var timeout;,它确实是公正的undefined.幸运的是,clearTimeout它的输入相当松懈:传递一个undefined计时器标识符会导致它什么都不做,它不会抛出错误或其他东西.
第二点由生成的函数完成.它首先在变量中存储有关调用的一些信息(this上下文和arguments),以便稍后可以将它们用于去抖动调用.然后它清除超时(如果有一组),然后创建一个新的替换它使用setTimeout.请注意,这会覆盖timeout多个函数调用的值,并且该值会持续存在!这允许去抖动实际工作:如果多次调用该函数,timeout则使用新的计时器多次覆盖.如果不是这种情况,多次呼叫将导致启动多个定时器,这些定时器都保持活动状态 - 呼叫只会被延迟,但不会被去抖动.
第三点是在超时回调中完成的.它取消设置timeout变量并使用存储的调用信息进行实际函数调用.
该immediate标志应该控制是否应该在定时器之前或之后调用该函数.如果是false,则直到计时器被命中后才调用原始函数.如果是true,则首先调用原始函数,并且在计时器被命中之前不再调用它.
但是,我确实认为if (immediate && !timeout)检查错误:timeout刚刚设置为返回的计时器标识符,setTimeout因此!timeout始终false在该点,因此永远不能调用该函数.当前版本的underscore.js似乎有一个稍微不同的检查,它immediate && !timeout 在调用之前进行评估setTimeout.(算法也有点不同,例如它不使用clearTimeout.)这就是为什么你应该总是尝试使用最新版本的库.:-)
jur*_*six 28
去抖动函数在调用时不会执行,它们会在执行前等待一段可配置的持续时间暂停调用; 每次新调用都会重新启动计时器.
限制函数执行,然后等待可配置的持续时间,然后再次触发.
去抖动非常适合按键事件; 当用户开始输入然后暂停时,您将所有按键提交为单个事件,从而减少处理调用.
对于您只希望允许用户在设定的一段时间内调用一次的实时端点,Throttle非常适合.
查看Underscore.js的实现.
小智 19
我在JavaScript中写了一篇名为Demistifying Debounce的帖子,其中我详细解释了debounce 功能的工作原理并包含了一个演示.
当我第一次遇到debounce函数时,我也没有完全理解debounce函数是如何工作的.虽然尺寸相对较小,但它们实际上采用了一些非常先进的JavaScript概念!掌握范围,封闭和setTimeout方法将有所帮助.
话虽如此,下面是我在上面引用的帖子中解释和演示的基本去抖函数.
成品
// Create JD Object
// ----------------
var JD = {};
// Debounce Method
// ---------------
JD.debounce = function(func, wait, immediate) {
var timeout;
return function() {
var context = this,
args = arguments;
var later = function() {
timeout = null;
if ( !immediate ) {
func.apply(context, args);
}
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait || 200);
if ( callNow ) {
func.apply(context, args);
}
};
};
Run Code Online (Sandbox Code Playgroud)
说明
// Create JD Object
// ----------------
/*
It's a good idea to attach helper methods like `debounce` to your own
custom object. That way, you don't pollute the global space by
attaching methods to the `window` object and potentially run in to
conflicts.
*/
var JD = {};
// Debounce Method
// ---------------
/*
Return a function, that, as long as it continues to be invoked, will
not be triggered. The function will be called after it stops being
called for `wait` milliseconds. If `immediate` is passed, trigger the
function on the leading edge, instead of the trailing.
*/
JD.debounce = function(func, wait, immediate) {
/*
Declare a variable named `timeout` variable that we will later use
to store the *timeout ID returned by the `setTimeout` function.
*When setTimeout is called, it retuns a numeric ID. This unique ID
can be used in conjunction with JavaScript's `clearTimeout` method
to prevent the code passed in the first argument of the `setTimout`
function from being called. Note, this prevention will only occur
if `clearTimeout` is called before the specified number of
milliseconds passed in the second argument of setTimeout have been
met.
*/
var timeout;
/*
Return an anomymous function that has access to the `func`
argument of our `debounce` method through the process of closure.
*/
return function() {
/*
1) Assign `this` to a variable named `context` so that the
`func` argument passed to our `debounce` method can be
called in the proper context.
2) Assign all *arugments passed in the `func` argument of our
`debounce` method to a variable named `args`.
*JavaScript natively makes all arguments passed to a function
accessible inside of the function in an array-like variable
named `arguments`. Assinging `arguments` to `args` combines
all arguments passed in the `func` argument of our `debounce`
method in a single variable.
*/
var context = this, /* 1 */
args = arguments; /* 2 */
/*
Assign an anonymous function to a variable named `later`.
This function will be passed in the first argument of the
`setTimeout` function below.
*/
var later = function() {
/*
When the `later` function is called, remove the numeric ID
that was assigned to it by the `setTimeout` function.
Note, by the time the `later` function is called, the
`setTimeout` function will have returned a numeric ID to
the `timeout` variable. That numeric ID is removed by
assiging `null` to `timeout`.
*/
timeout = null;
/*
If the boolean value passed in the `immediate` argument
of our `debouce` method is falsy, then invoke the
function passed in the `func` argument of our `debouce`
method using JavaScript's *`apply` method.
*The `apply` method allows you to call a function in an
explicit context. The first argument defines what `this`
should be. The second argument is passed as an array
containing all the arguments that should be passed to
`func` when it is called. Previously, we assigned `this`
to the `context` variable, and we assigned all arguments
passed in `func` to the `args` variable.
*/
if ( !immediate ) {
func.apply(context, args);
}
};
/*
If the value passed in the `immediate` argument of our
`debounce` method is truthy and the value assigned to `timeout`
is falsy, then assign `true` to the `callNow` variable.
Otherwise, assign `false` to the `callNow` variable.
*/
var callNow = immediate && !timeout;
/*
As long as the event that our `debounce` method is bound to is
still firing within the `wait` period, remove the numerical ID
(returned to the `timeout` vaiable by `setTimeout`) from
JavaScript's execution queue. This prevents the function passed
in the `setTimeout` function from being invoked.
Remember, the `debounce` method is intended for use on events
that rapidly fire, ie: a window resize or scroll. The *first*
time the event fires, the `timeout` variable has been declared,
but no value has been assigned to it - it is `undefined`.
Therefore, nothing is removed from JavaScript's execution queue
because nothing has been placed in the queue - there is nothing
to clear.
Below, the `timeout` variable is assigned the numerical ID
returned by the `setTimeout` function. So long as *subsequent*
events are fired before the `wait` is met, `timeout` will be
cleared, resulting in the function passed in the `setTimeout`
function being removed from the execution queue. As soon as the
`wait` is met, the function passed in the `setTimeout` function
will execute.
*/
clearTimeout(timeout);
/*
Assign a `setTimout` function to the `timeout` variable we
previously declared. Pass the function assigned to the `later`
variable to the `setTimeout` function, along with the numerical
value assigned to the `wait` argument in our `debounce` method.
If no value is passed to the `wait` argument in our `debounce`
method, pass a value of 200 milliseconds to the `setTimeout`
function.
*/
timeout = setTimeout(later, wait || 200);
/*
Typically, you want the function passed in the `func` argument
of our `debounce` method to execute once *after* the `wait`
period has been met for the event that our `debounce` method is
bound to (the trailing side). However, if you want the function
to execute once *before* the event has finished (on the leading
side), you can pass `true` in the `immediate` argument of our
`debounce` method.
If `true` is passed in the `immediate` argument of our
`debounce` method, the value assigned to the `callNow` variable
declared above will be `true` only after the *first* time the
event that our `debounce` method is bound to has fired.
After the first time the event is fired, the `timeout` variable
will contain a falsey value. Therfore, the result of the
expression that gets assigned to the `callNow` variable is
`true` and the function passed in the `func` argument of our
`debounce` method is exected in the line of code below.
Every subsequent time the event that our `debounce` method is
bound to fires within the `wait` period, the `timeout` variable
holds the numerical ID returned from the `setTimout` function
assigned to it when the previous event was fired, and the
`debounce` method was executed.
This means that for all subsequent events within the `wait`
period, the `timeout` variable holds a truthy value, and the
result of the expression that gets assigned to the `callNow`
variable is `false`. Therefore, the function passed in the
`func` argument of our `debounce` method will not be executed.
Lastly, when the `wait` period is met and the `later` function
that is passed in the `setTimeout` function executes, the
result is that it just assigns `null` to the `timeout`
variable. The `func` argument passed in our `debounce` method
will not be executed because the `if` condition inside the
`later` function fails.
*/
if ( callNow ) {
func.apply(context, args);
}
};
};
Run Code Online (Sandbox Code Playgroud)
Tha*_*you 15
我们现在都在使用 Promise
\n我见过的许多实现都使问题过于复杂或存在其他卫生问题。现在是 2021 年,我们已经使用 Promise 很长时间了 \xe2\x80\x93 并且有充分的理由。Promise 清理异步程序并减少发生错误的机会。在这篇文章中,我们将编写我们自己的debounce. 此实施将 -
我们debounce用它的两个参数来编写,即task去抖和延迟的毫秒数ms。我们为其本地状态引入一个本地绑定,t-
function debounce (task, ms) {\n let t = { promise: null, cancel: _ => void 0 }\n return async (...args) => {\n try {\n t.cancel()\n t = deferred(ms)\n await t.promise\n await task(...args)\n }\n catch (_) { /* prevent memory leak */ }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n我们依赖于一个可重用的deferred函数,它创建了一个在几毫秒内解析的新承诺ms。它引入了两个本地绑定,它promise本身,以及它的能力cancel-
function deferred (ms) {\n let cancel, promise = new Promise((resolve, reject) => {\n cancel = reject\n setTimeout(resolve, ms)\n })\n return { promise, cancel }\n}\nRun Code Online (Sandbox Code Playgroud)\n点击计数器示例
\n在第一个示例中,我们有一个用于计算用户点击次数的按钮。事件侦听器使用 附加debounce,因此计数器仅在指定的持续时间后递增 -
// debounce, deferred\nfunction debounce (task, ms) { let t = { promise: null, cancel: _ => void 0 }; return async (...args) => { try { t.cancel(); t = deferred(ms); await t.promise; await task(...args); } catch (_) { console.log("cleaning up cancelled promise") } } }\nfunction deferred (ms) { let cancel, promise = new Promise((resolve, reject) => { cancel = reject; setTimeout(resolve, ms) }); return { promise, cancel } }\n\n// dom references\nconst myform = document.forms.myform\nconst mycounter = myform.mycounter\n\n// event handler\nfunction clickCounter (event) {\n mycounter.value = Number(mycounter.value) + 1\n}\n\n// debounced listener\nmyform.myclicker.addEventListener("click", debounce(clickCounter, 1000))Run Code Online (Sandbox Code Playgroud)\r\n<form id="myform">\n<input name="myclicker" type="button" value="click" />\n<output name="mycounter">0</output>\n</form>Run Code Online (Sandbox Code Playgroud)\r\n实时查询示例,“自动完成”
\n在第二个示例中,我们有一个带有文本输入的表单。我们的search查询附有debounce-
// debounce, deferred\nfunction debounce (task, ms) { let t = { promise: null, cancel: _ => void 0 }; return async (...args) => { try { t.cancel(); t = deferred(ms); await t.promise; await task(...args); } catch (_) { console.log("cleaning up cancelled promise") } } }\nfunction deferred (ms) { let cancel, promise = new Promise((resolve, reject) => { cancel = reject; setTimeout(resolve, ms) }); return { promise, cancel } }\n\n// dom references\nconst myform = document.forms.myform\nconst myresult = myform.myresult\n\n// event handler\nfunction search (event) {\n myresult.value = `Searching for: ${event.target.value}`\n}\n\n// debounced listener\nmyform.myquery.addEventListener("keypress", debounce(search, 1000))Run Code Online (Sandbox Code Playgroud)\r\n<form id="myform">\n<input name="myquery" placeholder="Enter a query..." />\n<output name="myresult"></output>\n</form>Run Code Online (Sandbox Code Playgroud)\r\n多次去抖,反应钩子 useDebounce
\n在另一个问答中,有人问是否可以使用公开去抖取消机制并创建一个useDebounceReact hook。使用deferred上面的方法,这是一个微不足道的练习。
// revised implementation\nfunction debounce(task, ms) {\n let t = { promise: null, cancel: _ => void 0 }\n return [\n // ...,\n _ => t.cancel() // \xe2\x9c\x85 return cancellation mechanism\n ]\n}\nRun Code Online (Sandbox Code Playgroud)\n// revised usage\nconst [inc, cancel] = debounce(clickCounter, 1000) // \xe2\x9c\x85 two controls\nmyform.mybutton.addEventListener("click", inc)\nmyform.mycancel.addEventListener("click", cancel)\nRun Code Online (Sandbox Code Playgroud)\n实现useDebounceReact hook 轻而易举 -
function useDebounce(task, ms) {\n const [f, cancel] = debounce(task, ms)\n useEffect(_ => cancel) // \xe2\x9c\x85 auto-cancel when component unmounts\n return [f, cancel]\n}\nRun Code Online (Sandbox Code Playgroud)\n前往原始问答以获得完整的演示
\n| 归档时间: |
|
| 查看次数: |
67460 次 |
| 最近记录: |