为什么这个去抖功能不起作用?

时间:2017-11-22 00:35:54

标签: javascript

我正试图制作去抖功能,我不明白它为什么不去抖动。

我首先在这里创建了这个函数:

const debounce = function throttleFunctionCalls(func, wait, immediate) {
  let timeout
  return () => {
    const context = this
    const args = arguments
    const later = () => {
      timeout = null
      if (!immediate) func.apply(context, args)
    }
    const callNow = immediate && !timeout
    clearTimeout(timeout)
    timeout = setTimeout(later, wait)
    if (callNow) func.apply(context, args)
  }
}

const testFunction = () => console.log('test')

setInterval(() => debounce(testFunction(), 10000), 100)

但是,它每100毫秒记录一次,因此无法正常工作。

我尝试了一种完全不同的去抖功能,由于传播args而不是使用arguments,这可以说是更好的,但它遇到了同样的问题:

function debounce(func, wait) {
  let timeout
  return function throttleFunctionCalls(...args) {
    const context = this
    clearTimeout(timeout)
    timeout = setTimeout(() => func.apply(context, args), wait)
  }
}

const testFunction = () => console.log('test')

setInterval(() => debounce(testFunction(), 10000), 100)

我现在没有真正的去抖动,所以我在自然栖息地进行测试时遇到了麻烦。

setInterval方法有问题吗?

似乎它对timeout的认识每次都会迷失。任何人都能解释一下吗?

[edit]

正如charlietfl在下面的评论中指出的,我不应该唤起这个功能。哎呀,我绝对不应该这样做,但我在提出这个问题之前尝试了它并且它没有用,所以出于某种原因,我最终得到了上面的内容。

function debounce(func, wait) {
  let timeout
  return function throttleFunctionCalls(...args) {
    const context = this
    clearTimeout(timeout)
    timeout = setTimeout(() => func.apply(context, args), wait)
  }
}

const testFunction = (value) => console.log('test: ' + value)

setInterval(() => debounce(testFunction, 1), 100)

当我将参考函数作为参考传递时,上面挂起了这个线程。我也不确定如何将实际的函数参数传递给它,除非它通过闭包来获取它们。

任何人都可以向我展示一个可以通过testFunction()传递任意值的功能示例吗?

3 个答案:

答案 0 :(得分:2)

在通话中

setInterval(() => debounce(testFunction(), 10000), 100)

你a)将testFunction() - undefined的返回值传递给debounce,而不是函数本身,b)永远不要调用{{1}返回的函数}。这就是调用的样子:

debounce

或简而言之

const debouncedTestFunction = debounce(testFunction, 10000)
setInterval(() => debouncedTestFunction(), 100)

那就是说,你误用箭头语法。箭头函数没有自己的argumentsthis values,但是在词汇上继承它们,因此您的代码不起作用。它必须是

setInterval(debounce(testFunction, 10000), 100)

答案 1 :(得分:1)

代码或多或少完整但被错误地调用。 debounce正在返回已被去抖动的函数。您必须多次调用debounced函数而不是debouncedebounce本身提供的函数。

请注意,对于第一次调用的原始函数,对去抖动函数的调用必须停止的时间超过去抖动时间。即使它们永远不会停止,也可以使逻辑变得更加复杂,即使它们永远不会停止 - 但这通常不是硬件去抖电路的工作原理。

以下是您的行动代码示例:



function debounce(func, wait) {
  let timeout
  return function throttleFunctionCalls(...args) {
    const context = this
    clearTimeout(timeout)
    timeout = setTimeout(() => func.apply(context, args), wait)
  }
}

const testFunction = (string) => console.log('test ' + string)

const debouncedTestFunction = debounce( testFunction, 500);

setTimeout( debouncedTestFunction, 100, "first call");
setTimeout( debouncedTestFunction, 200, "second call");
setTimeout( debouncedTestFunction, 300, "third call");
// and pause for a while
setTimeout( debouncedTestFunction, 900, "fourth call");




由于为500毫秒去抖动选择了setTimeout延迟,预期的结果是第三次和第四次调用应该通过,但第一次和第二次调用不会。

<小时/> 后续操作:

在问题评论的@charlietfl中,当需要对该函数的引用时,不要调用testFunction

明智的做法是放弃在arguments的第二版中使用debounce数组。正如其他答案arguments中所述,箭头函数不支持它,但失败模式会很有趣 - debounce函数表达式以function开头,它将arguments引入函数范围。然后,返回的箭头函数将使用arguments调用的debounce值,如下例所示:

&#13;
&#13;
function test() {
  return ()=>arguments[0]
}
console.log( (test("test argument"))("call argument"));
&#13;
&#13;
&#13;

相当混乱。检查babel如何转换arguments值的箭头函数用法是有意义的。

第二个问题是(第二个)debounce函数有效地返回一个绑定函数(以Function.prototype.bind的方式),但记录thisdebounce的值。功能无论可能是什么。我建议您在thisValue时添加debounce参数,或在null调用thisValue时使用apply。 (null的确切效果取决于called code is in strict mode)。

答案 2 :(得分:1)

让我们看看你的最后一个例子:

function debounce(func, wait) {
  let timeout
  return function throttleFunctionCalls(...args) {
    const context = this
    clearTimeout(timeout)
    timeout = setTimeout(() => func.apply(context, args), wait)
  }
}

您已经定义了一个函数debounce,它接受​​一个函数和一个等待时间并返回一个函数。到目前为止一切都很好。

const testFunction = (value) => console.log('test: ' + value)

接下来,您已经定义了一个函数testFunction,它接受​​一个值并记录它。

setInterval(() => debounce(testFunction, 1), 100)

这是一个问题。作为setInterval的第一个参数,您创建了一个不接受任何参数的新函数,并返回在debounce上调用testFunction的结果。如上所述,debounce只会返回一个函数。实际上,每100毫秒一次,您只需返回一个新函数而不是执行日志记录逻辑。

以下是使用箭头功能的稍微浓缩的代码版本:

const debounce = (func, wait) => {
    let timeout;
    return (...args) => {
        clearTimeout(timeout);
        timeout = setTimeout(() => func.apply(this, args), wait);
    };
};

我们再次构建一个测试函数:

const testFunction = (value) => {
    console.log(`test: ${value}`);
};

这次我们将以1ms的等待时间制作测试功能的去抖版本:

const debouncedTest1 = debounce(testFunction, 1);

现在我们可以设置一个间隔来执行它。这也是您可以将参数传递给测试函数的方法之一:

setInterval(debouncedTest1, 200, 'foo');

这将记录“foo”&#39;每200ms。从技术上讲,第一条日志消息将在setInterval调用后201ms出现:间隔为200ms,去抖动逻辑为1ms。此处不会发生去抖动,因为间隔时间比去抖动等待时间长。

尝试相反的方法不会输出任何内容:

const debouncedTest2 = debounce(testFunction, 200);
setInterval(debouncedTest2, 1, 'bar');

1ms后,间隔超时将尝试呼叫debouncedTest2。去抖动逻辑将使其在执行日志之前等待200ms。但是1ms之后,下一个间隔超时将会触发,这将再次推迟去抖动间隔。这将无限期地继续下去,你永远不会看到任何消息。

事实上,尝试使用setInterval测试去抖动可能不是最好的方法。

让我们尝试更加人性化的等待时间,并快速连续多次手动调用去抖动函数:

const debouncedTest3 = debounce(testFunction, 1000);

debouncedTest3('hi');
debouncedTest3('hello');
debouncedTest3('hey');

如果您尝试运行此测试,它将等待大约一秒钟,然后输出test: hey。辩论逻辑抛弃了前两个电话。

在切线上,将参数传递给测试函数的另一种方法是将它们烘焙到去抖函数调用中。有多种方法可以解决这个问题。以下是两个例子:

const debouncedTest4 = debounce(testFunction, 1000);
const debouncedTest4WithArg = () => debouncedTest4('test arg');

debouncedTest4WithArg();
debouncedTest4WithArg();
debouncedTest4WithArg();

const debouncedTest5 = debounce(() => testFunction('test arg'), 1000);

debouncedTest5();
debouncedTest5();
debouncedTest5();